All Products
Search
Document Center

Alibaba Cloud Model Studio:SSML dan LaTeX

Last Updated:Oct 21, 2026

Gunakan SSML untuk mengatur karakteristik suara seperti kecepatan, nada, dan volume, serta LaTeX untuk membaca rumus dalam bahasa Mandarin. Pelajari batasan, contoh API, tag, dan simbol yang didukung.

Pengenalan fitur

SSML adalah bahasa markup sintesis ucapan berbasis XML. SSML mengatur karakteristik suara seperti jenis suara, kecepatan, nada, dan volume, mengontrol jeda, menentukan pelafalan dan cara baca, serta menambahkan musik latar dan efek suara. Fitur LaTeX CosyVoice mengubah rumus matematika dalam teks menjadi ucapan bahasa Mandarin.

Gunakan fitur ini untuk buku audio, pengumuman layanan pelanggan, pelafalan multibahasa, dan pendidikan daring. Model, suara, dan API untuk setiap fitur dijelaskan di bawah. Untuk memilih model, lihat Sintesis ucapan.

Batasan penggunaan

SSML

  • Model yang didukung: 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

  • Suara: Hanya suara hasil kloning dan suara sistem yang ditandai mendukung SSML dalam daftar suara Qwen-Audio-TTS atau daftar suara CosyVoice yang didukung.

  • API:
    • Java SDK (versi 2.20.3 atau lebih baru): panggilan non-streaming dan streaming unidireksional
    • Python SDK (versi 1.23.4 atau lebih baru): panggilan non-streaming dan streaming unidireksional
    • WebSocket API: atur parameter enable_ssml ke true, dan kirim satu event continue-task

LaTeX

  • Model yang didukung: cosyvoice-v3.5-plus, cosyvoice-v3.5-flash, cosyvoice-v3-plus, cosyvoice-v3-flash, cosyvoice-v2

  • Bahasa: hanya bahasa Mandarin.

  • Konten: gunakan tag dan simbol yang didukung. Blok kode matematika Markdown, seperti blok bertanda math, tidak didukung. Masukkan hanya rumus di dalam pembatas. Teks lain dapat dibaca secara keliru.

Penggunaan

SSML

Saat menggunakan SSML, ikuti aturan berikut:

  • Apit seluruh teks dengan <speak></speak>. Beberapa elemen <speak> dapat digunakan berurutan, tetapi tidak dapat bersarang.
  • Escape karakter khusus XML di dalam tag: " → &quot;, ' → &apos;, & → &amp;, < → &lt;, dan > → &gt;.

Mulai cepat

Contoh berikut menggunakan SSML untuk mengatur laju ucapan selama sintesis. Sebelum menjalankan kode, lengkapi prasyarat berikut:

  1. Dapatkan Kunci API
  2. Instal DashScope SDK (Python 1.23.4 atau lebih baru, Java 2.20.3 atau lebih baru). Untuk detailnya, lihat 安装SDK.

PentingModel cosyvoice-v3.5-plus dan cosyvoice-v3.5-flash saat ini hanya tersedia di wilayah Beijing dan dirancang khusus untuk skenario kloning suara (tidak tersedia voice preset). Sebelum menggunakan model ini, buat voice target dengan mengikuti petunjuk di Kloning suara.

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;

/**
 * Deskripsi fitur SSML:
 *     1. Hanya panggilan non-streaming dan panggilan streaming unidireksional yang mendukung fitur SSML
 * Gunakan suara kloning atau preset yang mendukung SSML (misalnya longanyang untuk cosyvoice-v3-flash). Lihat model yang didukung di halaman ini.
 */
public class Main {
    private static String model = "cosyvoice-v3-flash";
    private static String voice = "longanyang";

    public static void main(String[] args) {
        // URL wilayah Singapura. Ganti WorkspaceId dengan ID ruang kerja aktual Anda. URL berbeda-beda tergantung wilayah.
        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()
                        // Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/en/model-studio/get-api-key
                        // Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: .apiKey("sk-xxx")
                        .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                        .model(model)
                        .voice(voice)
                        .build();

        SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
        ByteBuffer audio = null;
        try {
            // Panggilan non-streaming, diblokir hingga audio dikembalikan
            // Karakter khusus perlu di-escape
            audio = synthesizer.call("<speak rate=\"2\">Laju bicara saya lebih cepat daripada orang biasa.</speak>");
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            // Tutup koneksi WebSocket ketika tugas selesai
            synthesizer.getDuplexApi().close(1000, "bye");
        }
        if (audio != null) {
            // Simpan data audio ke file lokal "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);
            }
        }

        // Transmisi teks pertama memerlukan pembentukan koneksi WebSocket, sehingga latensi paket pertama mencakup waktu penyiapan koneksi
        System.out.println(
                "[Metric] requestId: "
                        + synthesizer.getLastRequestId()
                        + ", latensi paket pertama (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;

/**
 * Deskripsi fitur SSML:
 *     1. Hanya panggilan non-streaming dan panggilan streaming unidireksional yang mendukung fitur SSML
 * Gunakan suara kloning atau preset yang mendukung SSML (misalnya longanyang untuk cosyvoice-v3-flash). Lihat model yang didukung di halaman ini.
 */
public class Main {
    private static String model = "cosyvoice-v3-flash";
    private static String voice = "longanyang";

    public static void main(String[] args) {
        // URL wilayah Singapura. Ganti WorkspaceId dengan ID ruang kerja aktual Anda. URL berbeda-beda tergantung wilayah.
        Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
        streamAudioDataToSpeaker();
        System.out.println("Audio telah disimpan ke file output.mp3");
        System.exit(0);
    }

    public static void streamAudioDataToSpeaker() {
        CountDownLatch latch = new CountDownLatch(1);
        final FileOutputStream[] fileOutputStream = new FileOutputStream[1];

        try {
            fileOutputStream[0] = new FileOutputStream("output.mp3");
        } catch (IOException e) {
            System.err.println("Gagal membuat file output: " + e.getMessage());
            return;
        }

        // Implementasikan antarmuka ResultCallback
        ResultCallback<SpeechSynthesisResult> callback = new ResultCallback<SpeechSynthesisResult>() {
            @Override
            public void onEvent(SpeechSynthesisResult result) {
                if (result.getAudioFrame() != null) {
                    // Tulis data audio ke file lokal
                    try {
                        byte[] audioData = result.getAudioFrame().array();
                        fileOutputStream[0].write(audioData);
                        fileOutputStream[0].flush();
                    } catch (IOException e) {
                        System.err.println("Gagal menulis data audio: " + e.getMessage());
                    }
                }
            }

            @Override
            public void onComplete() {
                System.out.println("Menerima Complete, sintesis ucapan selesai");
                closeFileOutputStream(fileOutputStream[0]);
                latch.countDown();
            }

            @Override
            public void onError(Exception e) {
                System.out.println("Terjadi kesalahan: " + e.toString());
                closeFileOutputStream(fileOutputStream[0]);
                latch.countDown();
            }
        };

        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/en/model-studio/get-api-key
                        // Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: .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 {
            // Panggilan streaming unidireksional, langsung mengembalikan null (hasil aktual dikirimkan secara asinkron melalui antarmuka callback), ambil audio biner secara real-time dari metode onEvent pada antarmuka callback
            // Karakter khusus perlu di-escape
            synthesizer.call("<speak rate=\"2\">Laju bicara saya lebih cepat daripada orang biasa.</speak>");
            // Tunggu hingga sintesis selesai
            latch.await();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            // Tutup koneksi WebSocket setelah tugas selesai
            try {
                synthesizer.getDuplexApi().close(1000, "bye");
            } catch (Exception e) {
                System.err.println("Gagal menutup koneksi WebSocket: " + e.getMessage());
            }

            // Pastikan aliran file ditutup
            closeFileOutputStream(fileOutputStream[0]);
        }

        // Transmisi teks pertama memerlukan pembentukan koneksi WebSocket, sehingga latensi paket pertama mencakup waktu penyiapan koneksi
        System.out.println(
                "[Metric] requestId: "
                        + synthesizer.getLastRequestId()
                        + ", latensi paket pertama (ms): "
                        + synthesizer.getFirstPackageDelay());
    }

    private static void closeFileOutputStream(FileOutputStream fileOutputStream) {
        try {
            if (fileOutputStream != null) {
                fileOutputStream.close();
            }
        } catch (IOException e) {
            System.err.println("Gagal menutup aliran file: " + e.getMessage());
        }
    }
}

Python SDK

# coding=utf-8
# Deskripsi fitur SSML:
#     1. Hanya panggilan non-streaming dan panggilan streaming unidireksional yang mendukung fitur SSML
# Gunakan suara kloning atau preset yang mendukung SSML (misalnya longanyang untuk cosyvoice-v3-flash). Lihat model yang didukung di halaman ini.

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

# Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/en/model-studio/get-api-key
# Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')

# URL wilayah Singapura. Ganti WorkspaceId dengan ID ruang kerja aktual Anda. URL berbeda-beda tergantung wilayah.
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"

# Buat instance SpeechSynthesizer dan masukkan parameter permintaan seperti model dan voice di konstruktor
synthesizer = SpeechSynthesizer(model=model, voice=voice)
# Panggilan non-streaming, diblokir hingga audio dikembalikan
# Karakter khusus perlu di-escape
audio = synthesizer.call("<speak rate=\"2\">Laju bicara saya lebih cepat daripada orang biasa.</speak>")

# Simpan audio ke file lokal
with open('output.mp3', 'wb') as f:
    f.write(audio)

# Transmisi teks pertama memerlukan pembentukan koneksi WebSocket, sehingga latensi paket pertama mencakup waktu penyiapan koneksi
print('[Metric] requestId: {}, latensi paket pertama: {} ms'.format(
    synthesizer.get_last_request_id(),
    synthesizer.get_first_package_delay()))
# coding=utf-8
# Deskripsi fitur SSML:
#     1. Hanya panggilan non-streaming dan panggilan streaming unidireksional yang mendukung fitur SSML
# Gunakan suara kloning atau preset yang mendukung SSML (misalnya longanyang untuk cosyvoice-v3-flash). Lihat model yang didukung di halaman ini.

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

# Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/en/model-studio/get-api-key
# Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')

# URL wilayah Singapura. Ganti WorkspaceId dengan ID ruang kerja aktual Anda. URL berbeda-beda tergantung wilayah.
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"

# Definisikan antarmuka callback
class Callback(ResultCallback):
    _player = None
    _stream = None

    def on_open(self):
        # Buka file output untuk menulis data audio
        self.file = open("output.mp3", "wb")
        print("Koneksi dibentuk: " + get_timestamp())

    def on_complete(self):
        print("Sintesis ucapan selesai, semua hasil telah diterima: " + get_timestamp())
        if hasattr(self, 'file') and self.file:
            self.file.close()
        self
        # Transmisi teks pertama memerlukan pembentukan koneksi WebSocket, sehingga latensi paket pertama mencakup waktu penyiapan koneksi
        print('[Metric] requestId: {}, latensi paket pertama: {} ms'.format(
            self.synthesizer.get_last_request_id(),
            self.synthesizer.get_first_package_delay()))

    def on_error(self, message: str):
        print(f"Terjadi kesalahan sintesis ucapan: {message}")
        if hasattr(self, 'file') and self.file:
            self.file.close()

    def on_close(self):
        print("Koneksi ditutup: " + 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() + " Panjang audio biner: " + str(len(data)))
        # Tulis data audio ke file
        self.file.write(data)

callback = Callback()

# Buat instance SpeechSynthesizer dan masukkan parameter permintaan seperti model dan voice di konstruktor
synthesizer = SpeechSynthesizer(
    model=model,
    voice=voice,
    callback=callback,
)

# Tetapkan instance synthesizer ke callback untuk digunakan di on_complete
callback.synthesizer = synthesizer

# Panggilan streaming unidireksional, kirim teks untuk sintesis, ambil audio biner secara real-time dari metode on_data pada antarmuka callback
# Karakter khusus perlu di-escape
synthesizer.call("<speak rate=\"2\">Laju bicara saya lebih cepat daripada orang biasa.</speak>")

WebSocket API

Java

Untuk pengembangan Java, gunakan Java DashScope SDK. Untuk detailnya, lihat Java SDK.

Contoh WebSocket Java ini memerlukan dependensi berikut:

  • Java-WebSocket
  • jackson-databind

Kelola dependensi ini dengan Maven atau 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'
}

Kode Java:

import com.fasterxml.jackson.databind.ObjectMapper;

import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;

import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.*;

/**
 * Deskripsi fitur SSML:
 *     1. Saat mengirim perintah run-task, atur parameter enable_ssml ke true untuk mengaktifkan dukungan SSML
 *     2. Kirim teks yang berisi SSML melalui perintah continue-task, dan hanya satu perintah continue-task yang diizinkan
 * Gunakan suara kloning atau preset yang mendukung SSML (misalnya longanyang untuk cosyvoice-v3-flash). Lihat model yang didukung di halaman ini.
 */
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("Koneksi dibentuk");

        // Kirim perintah run-task
        // Jika enable_ssml diatur ke true, hanya satu perintah continue-task yang diizinkan; jika tidak, kesalahan "Text request limit violated, expected 1." akan dikembalikan
        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("Menerima pesan dari server: " + message);
        try {
            // Uraikan pesan JSON
            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("Menerima event task-started dari server");

                        // Kirim perintah continue-task. Saat menggunakan SSML, perintah ini hanya dapat dikirim sekali
                        // Karakter khusus perlu di-escape
                        sendContinueTask("<speak rate=\\\"2\\\">Laju bicara saya lebih cepat daripada orang biasa.</speak>");

                        // Kirim perintah finish-task
                        sendFinishTask();
                    } else if ("task-finished".equals(event)) {
                        System.out.println("Menerima event task-finished dari server");
                        taskFinished = true;
                        closeConnection();
                    } else if ("task-failed".equals(event)) {
                        System.out.println("Tugas gagal: " + message);
                        closeConnection();
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("Terjadi kesalahan: " + e.getMessage());
        }
    }

    @Override
    public void onMessage(ByteBuffer message) {
        System.out.println("Menerima data audio biner, ukuran: " + message.remaining());

        try (FileOutputStream fos = new FileOutputStream(outputFile, true)) {
            byte[] buffer = new byte[message.remaining()];
            message.get(buffer);
            fos.write(buffer);
            System.out.println("Data audio ditulis ke file lokal " + outputFile);
        } catch (IOException e) {
            System.err.println("Gagal menulis data audio ke file lokal: " + e.getMessage());
        }
    }

    @Override
    public void onClose(int code, String reason, boolean remote) {
        System.out.println("Koneksi ditutup: " + reason + " (" + code + ")");
    }

    @Override
    public void onError(Exception ex) {
        System.err.println("Kesalahan: " + 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 {
            // Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/en/model-studio/get-api-key
            // Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: String apiKey = "sk-xxx"
            String apiKey = System.getenv("DASHSCOPE_API_KEY");
            if (apiKey == null || apiKey.isEmpty()) {
                System.err.println("Harap atur variabel lingkungan DASHSCOPE_API_KEY");
                return;
            }

            Map<String, String> headers = new HashMap<>();
            headers.put("Authorization", "bearer " + apiKey);
            // URL wilayah Singapura. Ganti WorkspaceId dengan ID ruang kerja aktual Anda. URL berbeda-beda tergantung wilayah.
            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("Gagal terhubung ke layanan WebSocket: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

Python

Untuk pengembangan Python, gunakan Python DashScope SDK. Untuk detailnya, lihat Python SDK.

Berikut adalah contoh WebSocket Python. Sebelum menjalankannya, instal dependensi yang diperlukan:

pip uninstall websocket-client
pip uninstall websocket
pip install websocket-client

PentingFile contoh Python tidak boleh diberi nama websocket.py karena nama ini menyebabkan konflik penamaan yang menghasilkan kesalahan (AttributeError: module 'websocket' has no attribute 'WebSocketApp'. Did you mean: 'WebSocket'?).

# Deskripsi fitur SSML:
#     1. Saat mengirim perintah run-task, atur parameter enable_ssml ke true untuk mengaktifkan dukungan SSML
#     2. Kirim teks yang berisi SSML melalui perintah continue-task, dan hanya satu perintah continue-task yang diizinkan
# Gunakan suara kloning atau preset yang mendukung SSML (misalnya longanyang untuk cosyvoice-v3-flash). Lihat model yang didukung di halaman ini.

import websocket
import json
import uuid
import os
import time

class TTSClient:
    def __init__(self, api_key, uri):
        """
    Menginisialisasi instans TTSClient

    Parameter:
        api_key (str): Kunci API untuk otentikasi
        uri (str): Alamat layanan WebSocket
    """
        self.api_key = api_key  # Ganti dengan Kunci API Anda
        self.uri = uri  # Ganti dengan alamat WebSocket Anda
        self.task_id = str(uuid.uuid4())  # Membuat ID tugas yang unik
        self.output_file = f"output_{int(time.time())}.mp3"  # Jalur file audio output
        self.ws = None  # Instans WebSocketApp
        self.task_started = False  # Apakah task-started telah diterima
        self.task_finished = False  # Apakah task-finished / task-failed telah diterima

    def on_open(self, ws):
        """
    Callback saat koneksi WebSocket dibuat
    Mengirim perintah run-task untuk memulai tugas sintesis suara
    """
        print("WebSocket terhubung")

        # Membuat perintah run-task
        run_task_cmd = {
            "header": {
                "action": "run-task",
                "task_id": self.task_id,
                "streaming": "duplex"
            },
            "payload": {
                "task_group": "audio",
                "task": "tts",
                "function": "SpeechSynthesizer",
                "model": "cosyvoice-v3-flash",
                "parameters": {
                    "text_type": "PlainText",
                    "voice": "longanyang",
                    "format": "mp3",
                    "sample_rate": 22050,
                    "volume": 50,
                    "rate": 1,
                    "pitch": 1,
                    # Jika enable_ssml diatur ke True, hanya satu perintah continue-task yang diizinkan; jika tidak, error "Text request limit violated, expected 1." akan dikembalikan
                    "enable_ssml": True
                },
                "input": {}
            }
        }

        # Mengirim perintah run-task
        ws.send(json.dumps(run_task_cmd))
        print("perintah run-task terkirim")

    def on_message(self, ws, message):
        """
    Callback saat pesan diterima
    Menangani pesan teks dan biner secara terpisah
    """
        if isinstance(message, str):
            # Memproses pesan teks JSON
            try:
                msg_json = json.loads(message)
                print(f"Menerima pesan JSON: {msg_json}")

                if "header" in msg_json:
                    header = msg_json["header"]

                    if "event" in header:
                        event = header["event"]

                        if event == "task-started":
                            print("Tugas dimulai")
                            self.task_started = True

                            # Mengirim perintah continue-task. Saat menggunakan SSML, perintah ini hanya dapat dikirim sekali
                            # Karakter khusus perlu di-escape
                            self.send_continue_task("<speak rate=\"2\">My speaking rate is faster than a normal person's.</speak>")

                            # Mengirim finish-task setelah continue-task dikirim
                            self.send_finish_task()

                        elif event == "task-finished":
                            print("Tugas selesai")
                            self.task_finished = True
                            self.close(ws)

                        elif event == "task-failed":
                            error_msg = msg_json.get("error_message", "Error tidak diketahui")
                            print(f"Tugas gagal: {error_msg}")
                            self.task_finished = True
                            self.close(ws)

            except json.JSONDecodeError as e:
                print(f"Penguraian JSON gagal: {e}")
        else:
            # Memproses pesan biner (data audio)
            print(f"Menerima pesan biner, ukuran: {len(message)} byte")
            with open(self.output_file, "ab") as f:
                f.write(message)
            print(f"Data audio ditulis ke file lokal {self.output_file}")

    def on_error(self, ws, error):
        """Callback saat terjadi error"""
        print(f"Error WebSocket: {error}")

    def on_close(self, ws, close_status_code, close_msg):
        """Callback saat koneksi ditutup"""
        print(f"WebSocket ditutup: {close_msg} ({close_status_code})")

    def send_continue_task(self, text):
        """Mengirim perintah continue-task dengan konten teks untuk disintesis"""
        cmd = {
            "header": {
                "action": "continue-task",
                "task_id": self.task_id,
                "streaming": "duplex"
            },
            "payload": {
                "input": {
                    "text": text
                }
            }
        }

        self.ws.send(json.dumps(cmd))
        print(f"perintah continue-task terkirim, konten teks: {text}")

    def send_finish_task(self):
        """Mengirim perintah finish-task untuk mengakhiri tugas sintesis suara"""
        cmd = {
            "header": {
                "action": "finish-task",
                "task_id": self.task_id,
                "streaming": "duplex"
            },
            "payload": {
                "input": {}
            }
        }

        self.ws.send(json.dumps(cmd))
        print("perintah finish-task terkirim")

    def close(self, ws):
        """Menutup koneksi secara aktif"""
        if ws and ws.sock and ws.sock.connected:
            ws.close()
            print("Koneksi ditutup secara aktif")

    def run(self):
        """Memulai klien WebSocket"""
        # Mengatur header permintaan (otentikasi)
        header = {
            "Authorization": f"bearer {self.api_key}",
            "X-DashScope-DataInspection": "enable"
        }

        # Membuat instans WebSocketApp
        self.ws = websocket.WebSocketApp(
            self.uri,
            header=header,
            on_open=self.on_open,
            on_message=self.on_message,
            on_error=self.on_error,
            on_close=self.on_close
        )

        print("Mendengarkan pesan WebSocket...")
        self.ws.run_forever()  # Memulai pendengar koneksi persisten

# Contoh penggunaan
if __name__ == "__main__":
    # Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/en/model-studio/get-api-key
    # Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: API_KEY = "sk-xxx"
    API_KEY = os.environ.get("DASHSCOPE_API_KEY")
    # URL wilayah Singapura. Ganti WorkspaceId dengan ID ruang kerja Anda yang sebenarnya. URL bervariasi menurut wilayah.
    SERVER_URI = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference/"

    client = TTSClient(API_KEY, SERVER_URI)
    client.run()

Go

// Deskripsi fitur SSML:
//     1. Saat mengirim perintah run-task, atur parameter enable_ssml ke true untuk mengaktifkan dukungan SSML
//     2. Kirim teks yang berisi SSML melalui perintah continue-task, dan hanya satu perintah continue-task yang diizinkan
// Gunakan suara kloning atau preset yang mendukung SSML (misalnya longanyang untuk cosyvoice-v3-flash). Lihat model yang didukung di halaman ini.

package main

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

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

const (
    // URL wilayah Singapura. Ganti WorkspaceId dengan ID ruang kerja aktual Anda. URL berbeda-beda tergantung wilayah.
    wsURL      = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference/"
    outputFile = "output.mp3"
)

func main() {
    // Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/en/model-studio/get-api-key
    // Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: apiKey := "sk-xxx"
    apiKey := os.Getenv("DASHSCOPE_API_KEY")

    // Hapus file output
    os.Remove(outputFile)
    os.Create(outputFile)

    // Hubungkan ke 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("Koneksi gagal, kode status HTTP: %d\n", resp.StatusCode)
        }
        fmt.Println("Koneksi gagal:", err)
        return
    }
    defer conn.Close()

    // Hasilkan ID tugas
    taskID := uuid.New().String()
    fmt.Printf("ID tugas yang dihasilkan: %s\n", taskID)

    // Kirim perintah run-task
    runTaskCmd := map[string]interface{}{
        "header": map[string]interface{}{
            "action":    "run-task",
            "task_id":   taskID,
            "streaming": "duplex",
        },
        "payload": map[string]interface{}{
            "task_group": "audio",
            "task":       "tts",
            "function":   "SpeechSynthesizer",
            "model":      "cosyvoice-v3-flash",
            "parameters": map[string]interface{}{
                "text_type":   "PlainText",
                "voice":       "longanyang",
                "format":      "mp3",
                "sample_rate": 22050,
                "volume":      50,
                "rate":        1,
                "pitch":       1,
                // Jika enable_ssml diatur ke true, hanya satu perintah continue-task yang diizinkan; jika tidak, kesalahan "Text request limit violated, expected 1." akan dikembalikan
                "enable_ssml": true,
            },
            "input": map[string]interface{}{},
        },
    }

    runTaskJSON, _ := json.Marshal(runTaskCmd)
    fmt.Printf("Mengirim perintah run-task: %s\n", string(runTaskJSON))

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

    textSent := false

    // Proses pesan
    for {
        messageType, message, err := conn.ReadMessage()
        if err != nil {
            fmt.Println("Gagal membaca pesan:", err)
            break
        }

        // Proses pesan biner
        if messageType == websocket.BinaryMessage {
            fmt.Printf("Menerima pesan biner, panjang: %d\n", len(message))
            file, _ := os.OpenFile(outputFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
            file.Write(message)
            file.Close()
            continue
        }

        // Proses pesan teks
        messageStr := string(message)
        fmt.Printf("Menerima pesan teks: %s\n", strings.ReplaceAll(messageStr, "\n", ""))

        // Penguraian JSON sederhana untuk mendapatkan jenis event
        var msgMap map[string]interface{}
        if json.Unmarshal(message, &msgMap) == nil {
            if header, ok := msgMap["header"].(map[string]interface{}); ok {
                if event, ok := header["event"].(string); ok {
                    fmt.Printf("Jenis event: %s\n", event)

                    switch event {
                    case "task-started":
                        fmt.Println("=== Menerima event task-started ===")

                        if !textSent {
                            // Kirim perintah continue-task. Saat menggunakan SSML, perintah ini hanya dapat dikirim sekali
                            continueTaskCmd := map[string]interface{}{
                                "header": map[string]interface{}{
                                    "action":    "continue-task",
                                    "task_id":   taskID,
                                    "streaming": "duplex",
                                },
                                "payload": map[string]interface{}{
                                    "input": map[string]interface{}{
                                        // Karakter khusus perlu di-escape
                                        "text": "<speak rate=\"2\">Laju bicara saya lebih cepat daripada orang biasa.</speak>",
                                    },
                                },
                            }

                            continueTaskJSON, _ := json.Marshal(continueTaskCmd)
                            fmt.Printf("Mengirim perintah continue-task: %s\n", string(continueTaskJSON))

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

                            textSent = true

                            // Tunda sebelum mengirim finish-task
                            time.Sleep(500 * time.Millisecond)

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

                            finishTaskJSON, _ := json.Marshal(finishTaskCmd)
                            fmt.Printf("Mengirim perintah finish-task: %s\n", string(finishTaskJSON))

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

                    case "task-finished":
                        fmt.Println("=== Tugas selesai ===")
                        return

                    case "task-failed":
                        fmt.Println("=== Tugas gagal ===")
                        if header["error_message"] != nil {
                            fmt.Printf("Pesan kesalahan: %s\n", header["error_message"])
                        }
                        return

                    case "result-generated":
                        fmt.Println("Menerima event result-generated")
                    }
                }
            }
        }
    }
}

C#

using System.Net.WebSockets;
using System.Text;
using System.Text.Json;

// Deskripsi fitur SSML:
//     1. Saat mengirim perintah run-task, atur parameter enable_ssml ke true untuk mengaktifkan dukungan SSML
//     2. Kirim teks yang berisi SSML melalui perintah continue-task, dan hanya satu perintah continue-task yang diizinkan
// Gunakan suara kloning atau preset yang mendukung SSML (misalnya longanyang untuk cosyvoice-v3-flash). Lihat model yang didukung di halaman ini.
class Program {
    // Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/en/model-studio/get-api-key
    // Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: private static readonly string ApiKey = "sk-xxx"
    private static readonly string ApiKey = Environment.GetEnvironmentVariable("DASHSCOPE_API_KEY") ?? throw new InvalidOperationException("Variabel lingkungan DASHSCOPE_API_KEY belum diatur.");

    // URL wilayah Singapura. Ganti WorkspaceId dengan ID ruang kerja aktual Anda. URL berbeda-beda tergantung wilayah.
    private const string WebSocketUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference/";
    // Jalur file output
    private const string OutputFilePath = "output.mp3";

    // Klien WebSocket
    private static ClientWebSocket _webSocket = new ClientWebSocket();
    // Sumber token pembatalan
    private static CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
    // ID tugas
    private static string? _taskId;
    // Apakah tugas telah dimulai
    private static TaskCompletionSource<bool> _taskStartedTcs = new TaskCompletionSource<bool>();

    static async Task Main(string[] args) {
        try {
            // Hapus file output
            ClearOutputFile(OutputFilePath);

            // Hubungkan ke layanan WebSocket
            await ConnectToWebSocketAsync(WebSocketUrl);

            // Mulai tugas penerimaan pesan
            Task receiveTask = ReceiveMessagesAsync();

            // Kirim perintah run-task
            _taskId = GenerateTaskId();
            await SendRunTaskCommandAsync(_taskId);

            // Tunggu event task-started
            await _taskStartedTcs.Task;

            // Kirim perintah continue-task. Saat menggunakan SSML, perintah ini hanya dapat dikirim sekali
            // Karakter khusus perlu di-escape
            await SendContinueTaskCommandAsync("<speak rate=\"2\">Laju bicara saya lebih cepat daripada orang biasa.</speak>");

            // Kirim perintah finish-task
            await SendFinishTaskCommandAsync(_taskId);

            // Tunggu hingga tugas penerimaan selesai
            await receiveTask;

            Console.WriteLine("Tugas selesai, koneksi ditutup.");
        } catch (OperationCanceledException) {
            Console.WriteLine("Tugas dibatalkan.");
        } catch (Exception ex) {
            Console.WriteLine($"Terjadi kesalahan: {ex.Message}");
        } finally {
            _cancellationTokenSource.Cancel();
            _webSocket.Dispose();
        }
    }

    private static void ClearOutputFile(string filePath) {
        if (File.Exists(filePath)) {
            File.WriteAllText(filePath, string.Empty);
            Console.WriteLine("File output dihapus.");
        } else {
            Console.WriteLine("File output tidak ada, tidak perlu dihapus.");
        }
    }

    private static async Task ConnectToWebSocketAsync(string url) {
        var uri = new Uri(url);
        if (_webSocket.State == WebSocketState.Connecting || _webSocket.State == WebSocketState.Open) {
            return;
        }

        // Atur header koneksi WebSocket
        _webSocket.Options.SetRequestHeader("Authorization", $"bearer {ApiKey}");
        _webSocket.Options.SetRequestHeader("X-DashScope-DataInspection", "enable");

        try {
            await _webSocket.ConnectAsync(uri, _cancellationTokenSource.Token);
            Console.WriteLine("Berhasil terhubung ke layanan WebSocket.");
        } catch (OperationCanceledException) {
            Console.WriteLine("Koneksi WebSocket dibatalkan.");
        } catch (Exception ex) {
            Console.WriteLine($"Koneksi WebSocket gagal: {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,
                // Jika enable_ssml diatur ke true, hanya satu perintah continue-task yang diizinkan; jika tidak, kesalahan "Text request limit violated, expected 1." akan dikembalikan
                enable_ssml = true
            },
            input = new { }
        });

        await SendJsonMessageAsync(command);
        Console.WriteLine("Perintah run-task dikirim.");
    }

    private static async Task SendContinueTaskCommandAsync(string text) {
        if (_taskId == null) {
            throw new InvalidOperationException("ID tugas belum diinisialisasi.");
        }

        var command = CreateCommand("continue-task", _taskId, "duplex", new {
            input = new {
                text
            }
        });

        await SendJsonMessageAsync(command);
        Console.WriteLine("Perintah continue-task dikirim.");
    }

    private static async Task SendFinishTaskCommandAsync(string taskId) {
        var command = CreateCommand("finish-task", taskId, "duplex", new {
            input = new { }
        });

        await SendJsonMessageAsync(command);
        Console.WriteLine("Perintah finish-task dikirim.");
    }

    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("Pengiriman pesan dibatalkan.");
        }
    }

    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("Tugas dimulai.");
                        _taskStartedTcs.TrySetResult(true);
                        break;
                    case "task-finished":
                        Console.WriteLine("Tugas selesai.");
                        _cancellationTokenSource.Cancel();
                        break;
                    case "task-failed":
                        Console.WriteLine("Tugas gagal: " + response.RootElement.GetProperty("header").GetProperty("error_message").GetString());
                        _cancellationTokenSource.Cancel();
                        break;
                    default:
                        // result-generated dapat ditangani di sini
                        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) {
                // Proses data biner
                Console.WriteLine("Menerima data biner...");

                // Simpan data biner ke 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("Penerimaan pesan dibatalkan.");
            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

Struktur direktori kode contoh adalah:

my-php-project/

├── composer.json

├── vendor/

└── index.php

File composer.json berisi dependensi berikut. Sesuaikan nomor versi sesuai kebutuhan:

{
    "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/"
        }
    }
}

File index.php berisi kode berikut:

<!-- Deskripsi fitur SSML: -->
<!--     1. Saat mengirim perintah run-task, atur parameter enable_ssml ke true untuk mengaktifkan dukungan SSML -->
<!--     2. Kirim teks yang berisi SSML melalui perintah continue-task, dan hanya satu perintah continue-task yang diizinkan -->
<!--     3. Hanya voice hasil kloning dari model cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2, serta voice sistem yang ditandai mendukung SSML dalam daftar voice, yang mendukung fitur SSML (misalnya, voice longanyang dari model cosyvoice-v3-flash) -->

<?php

require __DIR__ . '/vendor/autoload.php';

use Ratchet\Client\Connector;
use React\EventLoop\Loop;
use React\Socket\Connector as SocketConnector;

// Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/en/model-studio/get-api-key
// Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: $api_key = "sk-xxx"
$api_key = getenv("DASHSCOPE_API_KEY");
// URL wilayah Singapura. Ganti WorkspaceId dengan ID ruang kerja aktual Anda. URL berbeda-beda tergantung wilayah.
$websocket_url = 'wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference/'; // Alamat server WebSocket
$output_file = 'output.mp3'; // Jalur file output

$loop = Loop::get();

if (file_exists($output_file)) {
    // Hapus isi file
    file_put_contents($output_file, '');
}

// Buat konektor kustom
$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 "Terhubung ke server WebSocket\n";

    // Hasilkan ID tugas
    $taskId = generateTaskId();

    // Kirim perintah run-task
    sendRunTaskMessage($conn, $taskId);

    // Definisikan fungsi untuk mengirim perintah continue-task
    $sendContinueTask = function() use ($conn, $loop, $taskId) {
        // Kirim perintah continue-task. Saat menggunakan SSML, perintah ini hanya dapat dikirim sekali
        $continueTaskMessage = json_encode([
            "header" => [
                "action" => "continue-task",
                "task_id" => $taskId,
                "streaming" => "duplex"
            ],
            "payload" => [
                "input" => [
                    // Karakter khusus perlu di-escape
                    "text" => "<speak rate=\"2\">Laju bicara saya lebih cepat daripada orang biasa.</speak>"
                ]
            ]
        ]);
        $conn->send($continueTaskMessage);

        // Kirim perintah finish-task
        sendFinishTaskMessage($conn, $taskId);
    };

    // Bendera apakah event task-started telah diterima
    $taskStarted = false;

    // Dengarkan pesan
    $conn->on('message', function($msg) use ($conn, $sendContinueTask, $loop, &$taskStarted, $taskId, $output_file) {
        if ($msg->isBinary()) {
            // Tulis data biner ke file lokal
            file_put_contents($output_file, $msg->getPayload(), FILE_APPEND);
        } else {
            // Proses pesan non-biner
            $response = json_decode($msg, true);

            if (isset($response['header']['event'])) {
                handleEvent($conn, $response, $sendContinueTask, $loop, $taskId, $taskStarted);
            } else {
                echo "Format pesan tidak dikenal\n";
            }
        }
    });

    // Dengarkan penutupan koneksi
    $conn->on('close', function($code = null, $reason = null) {
        echo "Koneksi ditutup\n";
        if ($code !== null) {
            echo "Kode tutup: " . $code . "\n";
        }
        if ($reason !== null) {
            echo "Alasan tutup: " . $reason . "\n";
        }
    });
}, function ($e) {
    echo "Tidak dapat terhubung: {$e->getMessage()}\n";
});

$loop->run();

/**
 * Hasilkan ID tugas
 * @return string
 */
function generateTaskId(): string {
    return bin2hex(random_bytes(16));
}

/**
 * Kirim perintah run-task
 * @param $conn
 * @param $taskId
 */
function sendRunTaskMessage($conn, $taskId) {
    $runTaskMessage = json_encode([
        "header" => [
            "action" => "run-task",
            "task_id" => $taskId,
            "streaming" => "duplex"
        ],
        "payload" => [
            "task_group" => "audio",
            "task" => "tts",
            "function" => "SpeechSynthesizer",
            "model" => "cosyvoice-v3-flash",
            "parameters" => [
                "text_type" => "PlainText",
                "voice" => "longanyang",
                "format" => "mp3",
                "sample_rate" => 22050,
                "volume" => 50,
                "rate" => 1,
                "pitch" => 1,
                // Jika enable_ssml diatur ke true, hanya satu perintah continue-task yang diizinkan; jika tidak, kesalahan "Text request limit violated, expected 1." akan dikembalikan
                "enable_ssml" => true
            ],
            "input" => (object) []
        ]
    ]);
    echo "Bersiap mengirim perintah run-task: " . $runTaskMessage . "\n";
    $conn->send($runTaskMessage);
    echo "Perintah run-task dikirim\n";
}

/**
 * Baca file audio
 * @param string $filePath
 * @return bool|string
 */
function readAudioFile(string $filePath) {
    $voiceData = file_get_contents($filePath);
    if ($voiceData === false) {
        echo "Tidak dapat membaca file audio\n";
    }
    return $voiceData;
}

/**
 * Pisahkan data audio
 * @param string $data
 * @param int $chunkSize
 * @return array
 */
function splitAudioData(string $data, int $chunkSize): array {
    return str_split($data, $chunkSize);
}

/**
 * Kirim perintah finish-task
 * @param $conn
 * @param $taskId
 */
function sendFinishTaskMessage($conn, $taskId) {
    $finishTaskMessage = json_encode([
        "header" => [
            "action" => "finish-task",
            "task_id" => $taskId,
            "streaming" => "duplex"
        ],
        "payload" => [
            "input" => (object) []
        ]
    ]);
    echo "Bersiap mengirim perintah finish-task: " . $finishTaskMessage . "\n";
    $conn->send($finishTaskMessage);
    echo "Perintah finish-task dikirim\n";
}

/**
 * Tangani event
 * @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 "Tugas dimulai, mengirim perintah continue-task...\n";
            $taskStarted = true;
            // Kirim perintah continue-task
            $sendContinueTask();
            break;
        case 'result-generated':
            // Abaikan event result-generated
            break;
        case 'task-finished':
            echo "Tugas selesai\n";
            $conn->close();
            break;
        case 'task-failed':
            echo "Tugas gagal\n";
            echo "Kode kesalahan: " . $response['header']['error_code'] . "\n";
            echo "Pesan kesalahan: " . $response['header']['error_message'] . "\n";
            $conn->close();
            break;
        case 'error':
            echo "Kesalahan: " . $response['payload']['message'] . "\n";
            break;
        default:
            echo "Event tidak dikenal: " . $response['header']['event'] . "\n";
            break;
    }

    // Jika tugas selesai, tutup koneksi
    if ($response['header']['event'] == 'task-finished') {
        // Tunggu 1 detik untuk memastikan semua data telah ditransmisikan
        $loop->addTimer(1, function() use ($conn) {
            $conn->close();
            echo "Klien menutup koneksi\n";
        });
    }

    // Jika event task-started belum diterima, tutup koneksi
    if (!$taskStarted && in_array($response['header']['event'], ['task-failed', 'error'])) {
        $conn->close();
    }
}

Node.js

Instal dependensi yang diperlukan:

npm install ws
npm install uuid

Kode contoh:

// Deskripsi fitur SSML:
//     1. Saat mengirim perintah run-task, atur parameter enable_ssml ke true untuk mengaktifkan dukungan SSML
//     2. Kirim teks yang berisi SSML melalui perintah continue-task, dan hanya satu perintah continue-task yang diizinkan
// Gunakan suara kloning atau preset yang mendukung SSML (misalnya longanyang untuk cosyvoice-v3-flash). Lihat model yang didukung di halaman ini.

import fs from 'fs';
import WebSocket from 'ws';
import { v4 as uuid } from 'uuid'; // Untuk menghasilkan UUID

// Kunci API untuk wilayah Singapura dan Beijing berbeda. Dapatkan Kunci API: https://www.alibabacloud.com/help/en/model-studio/get-api-key
// Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: const apiKey = "sk-xxx"
const apiKey = process.env.DASHSCOPE_API_KEY;
// URL wilayah Singapura. Ganti WorkspaceId dengan ID ruang kerja aktual Anda. URL berbeda-beda tergantung wilayah.
const url = 'wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference/';
// Jalur file output
const outputFilePath = 'output.mp3';

// Hapus file output
fs.writeFileSync(outputFilePath, '');

// Buat klien WebSocket
const ws = new WebSocket(url, {
  headers: {
    Authorization: `bearer ${apiKey}`,
    'X-DashScope-DataInspection': 'enable'
  }
});

let taskStarted = false;
let taskId = uuid();

ws.on('open', () => {
  console.log('Terhubung ke server WebSocket');

  // Kirim perintah run-task
  const runTaskMessage = JSON.stringify({
    header: {
      action: 'run-task',
      task_id: taskId,
      streaming: 'duplex'
    },
    payload: {
      task_group: 'audio',
      task: 'tts',
      function: 'SpeechSynthesizer',
      model: 'cosyvoice-v3-flash',
      parameters: {
        text_type: 'PlainText',
        voice: 'longanyang', // Voice
        format: 'mp3', // Format audio
        sample_rate: 22050, // Laju sampel
        volume: 50, // Volume
        rate: 1, // Laju ucapan
        pitch: 1, // Pitch
        enable_ssml: true // Apakah akan mengaktifkan SSML. Jika enable_ssml diatur ke true, hanya satu perintah continue-task yang diizinkan; jika tidak, kesalahan "Text request limit violated, expected 1." akan dikembalikan
      },
      input: {}
    }
  });
  ws.send(runTaskMessage);
  console.log('Pesan run-task dikirim');
});

const fileStream = fs.createWriteStream(outputFilePath, { flags: 'a' });
ws.on('message', (data, isBinary) => {
  if (isBinary) {
    // Tulis data biner ke file
    fileStream.write(data);
  } else {
    const message = JSON.parse(data);

    switch (message.header.event) {
      case 'task-started':
        taskStarted = true;
        console.log('Tugas dimulai');
        // Kirim perintah continue-task
        sendContinueTasks(ws);
        break;
      case 'task-finished':
        console.log('Tugas selesai');
        ws.close();
        fileStream.end(() => {
          console.log('Aliran file ditutup');
        });
        break;
      case 'task-failed':
        console.error('Tugas gagal:', message.header.error_message);
        ws.close();
        fileStream.end(() => {
          console.log('Aliran file ditutup');
        });
        break;
      default:
        // result-generated dapat ditangani di sini
        break;
    }
  }
});

function sendContinueTasks(ws) {

  if (taskStarted) {
    // Kirim perintah continue-task. Saat menggunakan SSML, perintah ini hanya dapat dikirim sekali
    const continueTaskMessage = JSON.stringify({
      header: {
        action: 'continue-task',
        task_id: taskId,
        streaming: 'duplex'
      },
      payload: {
        input: {
          // Karakter khusus perlu di-escape
          text: '<speak rate="2">Laju bicara saya lebih cepat daripada orang biasa.</speak>'
        }
      }
    });
    ws.send(continueTaskMessage);

    // Kirim perintah finish-task
    const finishTaskMessage = JSON.stringify({
      header: {
        action: 'finish-task',
        task_id: taskId,
        streaming: 'duplex'
      },
      payload: {
        input: {}
      }
    });
    ws.send(finishTaskMessage);
  }
}

ws.on('close', () => {
  console.log('Terputus dari server WebSocket');
});

LaTeX

Penggunaan

Apit rumus matematika dengan pembatas, lalu panggil API sintesis ucapan.

Pembatas yang didukung (pilih salah satu; efeknya sama):

  • $...$
  • $$...$$
  • \(...\)
  • \[...\]

Contoh input:

这是一道分数:$\frac{1}{2}$。

Pecahan dibaca dalam bahasa Mandarin sebagai “二分之一” (setengah).

Gunakan pembatas yang sama untuk rumus dengan beberapa operator, misalnya:

这是一道一元二次方程的求根公式:$x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}$,请仔细计算。

Dalam JSON dan string biasa pada bahasa seperti Python, Java, dan JavaScript, tulis garis miring terbalik \ sebagai \\. Misalnya, tulis perintah LaTeX \frac sebagai \\frac dalam string biasa.

Mulai cepat

Contoh Python ini menyintesis teks Mandarin yang berisi pecahan dan menyimpannya sebagai output.mp3. Lihat mulai cepat SSML untuk pengaturan kunci API dan SDK.

# coding=utf-8

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

# Jika variabel lingkungan belum diatur, ganti baris berikut dengan dashscope.api_key = "sk-xxx".
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')

# Ganti WorkspaceId dengan ID ruang kerja Anda. Gunakan URL untuk wilayah Anda.
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 setiap garis miring terbalik sebagai \\ dalam string biasa.
audio = synthesizer.call("这是一道分数:$\\frac{1}{2}$。")

with open('output.mp3', 'wb') as f:
    f.write(audio)

print('requestId: {}'.format(synthesizer.get_last_request_id()))

Tag SSML

CatatanImplementasi ini berdasarkan spesifikasi W3C SSML 1.0. Hanya tag yang tercantum di bawah yang didukung.

<speak>: Elemen akar

<speak> adalah elemen root untuk semua konten SSML. Semua teks harus diapit oleh tag <speak></speak>.

<speak>Teks yang memerlukan pemrosesan SSML</speak>

Atribut

Atribut

Tipe

Wajib

Deskripsi

voice

String

Tidak

Menentukan voice.

Atribut ini memiliki prioritas lebih tinggi daripada parameter voice dalam permintaan API.

rate

String

Tidak

Menentukan laju ucapan. Atribut ini memiliki prioritas lebih tinggi daripada parameter speech_rate dalam permintaan API.

  • Nilai valid: angka desimal antara 0,5 dan 2 (inklusif)

  • Nilai default: 1

    • Nilai lebih besar dari 1 meningkatkan laju ucapan
    • Nilai kurang dari 1 menurunkan laju ucapan

pitch

String

Tidak

Menentukan pitch. Atribut ini memiliki prioritas lebih tinggi daripada parameter pitch_rate dalam permintaan API.

  • Nilai valid: angka desimal antara 0,5 dan 2 (inklusif)

  • Nilai default: 1

    • Nilai lebih besar dari 1 meningkatkan pitch
    • Nilai kurang dari 1 menurunkan pitch

volume

String

Tidak

Menentukan volume. Atribut ini memiliki prioritas lebih tinggi daripada parameter volume dalam permintaan API.

  • Nilai valid: bilangan bulat antara 0 dan 100 (inklusif)

  • Nilai default: 50

    • Nilai lebih besar dari 50 meningkatkan volume
    • Nilai kurang dari 50 menurunkan volume

effect

String

Tidak

Menentukan efek audio.

  • Nilai valid:

    • robot: efek voice robot
    • lolita: efek voice lolita
    • lowpass: efek filter low-pass
    • echo: efek gema
    • eq: equalizer (lanjutan)
    • lpfilter: filter low-pass (lanjutan)
    • hpfilter: filter high-pass (lanjutan)

    Catatan

    • eq, lpfilter, dan hpfilter adalah jenis efek lanjutan. Gunakan parameter effectValue untuk menyesuaikan efek spesifik.
    • Setiap tag SSML hanya mendukung satu efek. Mengatur beberapa atribut effect secara bersamaan tidak diizinkan.
    • Mengaktifkan efek audio meningkatkan latensi sintesis.

effectValue

String

Tidak

Mengonfigurasi perilaku spesifik efek audio (parameter effect). Berlaku untuk tiga jenis efek lanjutan: eq, lpfilter, dan hpfilter.

  • Nilai valid:

    • eq (equalizer): sistem mendukung 8 pita frekuensi secara default, sesuai dengan frekuensi berikut:

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

      Setiap pita memiliki bandwidth 1,0q.

      Gunakan parameter effectValue untuk menentukan gain untuk setiap pita. Parameter ini adalah string 8 bilangan bulat berkisar antara -20 hingga 20, dipisahkan spasi. Nilai 0 berarti tidak ada penyesuaian gain untuk frekuensi tersebut.

      Contoh: effectValue="1 1 1 1 1 1 1 1"

    • lpfilter (filter low-pass): menentukan frekuensi cutoff. Nilai valid: bilangan bulat dalam rentang (0, target_sample_rate/2]. Contoh: effectValue="800".

    • hpfilter (filter high-pass): menentukan frekuensi cutoff. Nilai valid: bilangan bulat dalam rentang (0, target_sample_rate/2]. Contoh: effectValue="1200".

bgm

String

Tidak

Menambahkan musik latar pada ucapan yang disintesis.

Simpan file audio di Alibaba Cloud OSS dan pastikan dapat diakses melalui URL Internet. Lihat Unggah objek. Untuk objek privat, gunakan URL bertanda tangan yang masih berlaku. Escape karakter khusus XML pada URL, misalnya mengganti & dengan &amp;.

  • Persyaratan audio:

    Tidak ada batas atas ukuran file musik latar, tetapi file yang lebih besar memerlukan waktu unduh lebih lama. Jika ucapan yang disintesis lebih panjang daripada musik latar, musik akan diputar ulang secara otomatis.

    • Laju sampel: 16 kHz

    • Saluran: mono

    • Format: WAV

      Untuk mengonversi file non-WAV, gunakan ffmpeg:

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

PentingPengunggah bertanggung jawab secara hukum atas hak cipta audio yang diunggah.

backgroundMusicVolume

String

Tidak

Menentukan volume musik latar. Gunakan atribut ini bersama dengan atribut bgm.

Contoh

Tanpa atribut

<speak>
  Teks yang memerlukan tag SSML
</speak>

Atribut gabungan (dipisahkan spasi)

<speak rate="1.5" pitch="0.8" volume="80">
  Jadi ketika digabungkan, suara saya terdengar seperti ini.
</speak>

voice

<speak voice="longcheng_v2">
  Saya adalah voice pria.
</speak>

rate

<speak rate="2">
  Laju ucapan saya lebih cepat daripada normal.
</speak>

pitch

<speak pitch="0.5">
  Namun, pitch saya lebih rendah daripada orang lain.
</speak>

volume

<speak volume="80">
  Volume saya juga sangat tinggi.
</speak>

effect

<speak effect="robot">
  Apakah Anda menyukai robot WALL-E?
</speak>

effectValue

<speak effect="eq" effectValue="1 -20 1 1 1 1 20 1">
  Apakah Anda menyukai robot WALL-E?
</speak>

<speak effect="lpfilter" effectValue="1200">
  Apakah Anda menyukai robot WALL-E?
</speak>

<speak effect="hpfilter" effectValue="1200">
  Apakah Anda menyukai robot WALL-E?
</speak>

bgm

<speak bgm="https://nls.alicdn.com/bgm/2.wav" backgroundMusicVolume="30" rate="0.8" volume="40">
  <break time="2s"/>
  Pohon-pohon tua di tebing yang teduh diselimuti kabut
  <break time="700ms"/>
  Suara hujan masih terdengar di hutan bambu
  <break time="700ms"/>
  Saya tahu bahwa kapas berkontribusi pada rencana negara
  <break time="700ms"/>
  Pemandangan Mianzhou selalu menyedihkan
  <break time="2s"/>
</speak>

Hubungan tag

Tag <speak> dapat berisi teks dan tag anak berikut:

  • <break>: Kontrol durasi jeda
  • <sub>: Ganti teks
  • <phoneme>: Tentukan pelafalan (pinyin/fonetik)
  • <soundEvent>: Sisipkan suara eksternal (nada dering, suara kucing, dll.)
  • <say-as>: Atur interpretasi teks (angka, tanggal, nomor telepon, dll.)

<break>: Kontrol jeda

Menyisipkan jeda diam selama sintesis ucapan untuk mensimulasikan jeda alami dalam percakapan. Mendukung satuan waktu detik (s) dan milidetik (ms).

<!-- Tanpa atribut -->
<break/>
<!-- Dengan atribut time -->
<break time="string"/>

Atribut

Atribut

Tipe

Wajib

Deskripsi

time

String

Tidak

Menentukan durasi jeda, dalam detik atau milidetik (misalnya, "2s" atau "50ms").

  • Nilai valid:

    • Dalam detik (s): bilangan bulat antara 1 dan 10 (inklusif)
    • Dalam milidetik (ms): bilangan bulat antara 50 dan 10000 (inklusif)

Saat beberapa tag <break> digunakan secara berurutan, total durasi jeda adalah jumlah semua durasi individual. Jika total melebihi 10 detik, hanya 10 detik pertama yang diterapkan.

Tanpa atribut, <break> menyisipkan jeda 1 detik.

Contoh

time

<speak>
  Silakan tutup mata Anda dan beristirahat.<break time="500ms"/>Baiklah, silakan buka mata Anda.
</speak>

Tiga tag <break> berurutan di bawah menetapkan total jeda 15 detik. Jeda aktual dibatasi hingga 10 detik.

<speak>
  Silakan tutup mata Anda dan beristirahat.<break time="5s"/><break time="5s"/><break time="5s"/>Baiklah, silakan buka mata Anda.
</speak>

Hubungan tag

<break> adalah elemen self-closing dan tidak dapat berisi elemen anak.

<sub>: Ganti teks

Mengganti teks tertentu dengan konten yang lebih cocok untuk ucapan. Misalnya, membaca "W3C" sebagai "World Wide Web Consortium."

<sub alias="string"></sub>

Atribut

Atribut

Type

Wajib

Deskripsi

alias

String

Ya

Menentukan teks pengganti yang akan dibaca keras.

Contoh

alias

<speak>
  <sub alias="protokol jaringan">W3C</sub>
</speak>

Hubungan tag

Tag <sub> hanya dapat berisi teks biasa.

<phoneme>: Tentukan pelafalan

Memberikan kontrol tepat atas cara teks dilafalkan. Teks Tionghoa mendukung notasi pinyin, dan teks Inggris mendukung notasi fonetik CMU. Ini berguna untuk menghilangkan ambiguitas karakter polifonik dan menangani pelafalan bahasa asing.

<phoneme alphabet="string" ph="string">Teks</phoneme>

Atribut

Atribut

Tipe

Wajib

Deskripsi

alphabet

String

Ya

Menentukan jenis pelafalan: pinyin (untuk Tionghoa) atau simbol fonetik (untuk Inggris).

Nilai valid:

ph

String

Ya

Menentukan notasi pinyin atau fonetik yang tepat. Aturan penggunaan:

  • Pisahkan pinyin untuk beberapa karakter dengan spasi. Jumlah entri pinyin harus sesuai dengan jumlah karakter.
  • Setiap entri pinyin terdiri dari pelafalan dan nomor nada. Nomor nada berkisar dari 1 hingga 5, di mana 5 mewakili nada netral.

Contoh

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>

Hubungan tag

Tag <phoneme> hanya dapat berisi teks biasa.

<soundEvent>: Sisipkan efek suara

Menyisipkan file efek suara (seperti nada peringatan atau suara latar) pada titik tertentu dalam ucapan untuk memperkaya output audio.

<soundEvent src="URL"/>

Atribut

Atribut

Tipe

Wajib

Deskripsi

src

String

Ya

Menentukan URL file audio eksternal.

Simpan file audio di Alibaba Cloud OSS dan pastikan dapat diakses melalui URL Internet. Lihat Unggah objek. Untuk objek privat, gunakan URL bertanda tangan yang masih berlaku. Escape karakter khusus XML pada URL, misalnya mengganti & dengan &amp;.

  • Persyaratan audio:

    • Laju sampel: 16 kHz

    • Saluran: mono

    • Format: WAV

      Untuk mengonversi file non-WAV, gunakan ffmpeg:

      ffmpeg -i input_audio -acodec pcm_s16le -ac 1 -ar 16000 output.wav
      
    • Ukuran file: maksimum 2 MB

    • Kedalaman bit: 16-bit

PentingPengunggah bertanggung jawab secara hukum atas hak cipta audio yang diunggah.

Contoh

src

<speak>
  Seekor kuda ketakutan<soundEvent src="https://nls.alicdn.com/sound-event/horse-neigh.wav"/>dan orang-orang tersebar untuk menghindarinya.
</speak>

Hubungan tag

<soundEvent> adalah elemen self-closing dan tidak dapat berisi elemen anak.

<say-as>: Atur cara membaca teks

Menentukan jenis konten teks (seperti angka, tanggal, atau nomor telepon) sehingga sistem membacanya sesuai aturan yang sesuai untuk jenis tersebut.

<say-as interpret-as="string">Teks</say-as>

Atribut

Atribut

Tipe

Wajib

Deskripsi

interpret-as

String

Ya

Menentukan jenis konten teks di dalam tag.

Nilai valid:

  • cardinal: dibaca sebagai bilangan bulat atau desimal standar
  • digits: membaca setiap digit secara individual (misalnya, 123 dibaca sebagai "satu dua tiga")
  • telephone: membaca digit demi digit dalam format nomor telepon standar
  • name: membaca menggunakan aturan pelafalan nama standar
  • address: membaca menggunakan aturan pelafalan alamat standar
  • id: membaca menggunakan aturan pelafalan pengenal (nama akun, nama panggilan) standar
  • characters: membaca setiap karakter dalam teks secara individual
  • punctuation: membaca nama setiap tanda baca
  • date: membaca menggunakan aturan pelafalan tanggal standar
  • time: membaca menggunakan aturan pelafalan waktu standar
  • currency: membaca menggunakan aturan pelafalan jumlah moneter standar
  • measure: membaca menggunakan aturan pelafalan satuan ukuran standar

Lihat format say-as yang didukung

cardinal

Mandarin

Format

Contoh

Output Mandarin

Catatan

String digit

145

一百四十五

Bilangan bulat positif dan negatif hingga 20 digit: [-99999999999999999999,99999999999999999999]. Tidak ada batas khusus untuk jumlah angka desimal; disarankan maksimal 10.

Tanda minus+String digit

-145

负一百四十五

Kelompok 3 digit dipisahkan koma

10,000

一万

Tanda minus+Kelompok 3 digit dipisahkan koma

-10,124

负一万一百二十四

String digit+Titik desimal+Dua nol

10.00

十

Tanda minus+String digit+Titik desimal+Dua nol

-110.00

负一百一十

String digit+Titik desimal+String digit

79.090

七十九点零九零

Tanda minus+String digit+Titik desimal+String digit

-79.001

负七十九点零零一

Inggris

Format

Contoh

English output

Catatan

String digit

145

one hundred forty five

Rentang integer: bilangan bulat positif dan negatif hingga 13 digit, [-999999999999,999999999999].

Rentang desimal: tidak ada batas spesifik tempat desimal, tetapi disarankan 10 atau kurang.

String digit diawali nol

0145

one hundred forty five

Tanda minus + string digit

-145

minus hundred forty five

String digit dengan koma memisahkan setiap 3 digit

60,000

sixty thousand

Tanda minus + string digit dipisahkan koma

-208,000

minus two hundred eight thousand

String digit + titik desimal + nol

12.00

twelve

String digit + titik desimal + string digit

12.34

twelve point three four

String digit dipisahkan koma + titik desimal + string digit

1,000.1

one thousand point one

Tanda minus + string digit + titik desimal + string digit

-12.34

minus twelve point three four

Tanda minus + string digit dipisahkan koma + titik desimal + string digit

-1,000.1

minus one thousand point one

(String digit dipisahkan koma) + tanda hubung + (string digit dipisahkan koma)

1-1,000

one to one thousand

Pembacaan default lainnya

012.34

twelve point three four

Tidak ada

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

Mandarin

Format

Contoh

Output Mandarin

Catatan

String digit

129090909

一二九零九零九零九

Panjang string digit tidak dibatasi secara khusus; disarankan maksimal 20 digit. Jika melebihi 10 digit, jeda disisipkan setelah setiap digit.

Inggris

Format

Contoh

Output Inggris

Catatan

String digit

12034

one two zero three four

Tidak ada batas spesifik panjang string digit, tetapi disarankan 20 digit atau kurang.

Saat string digit dikelompokkan dengan spasi atau tanda hubung, jeda koma disisipkan antar grup. Maksimal 5 grup didukung.

String digit + spasi/tanda hubung + string digit + spasi/tanda hubung + string digit + spasi/tanda hubung + string digit

1-23-456 7890

one, two three, four five six, seven eight nine zero

telephone

Mandarin

Format

Contoh

Output Mandarin

Catatan

Nomor telepon tetap

4930286

四九三 零二八六

Mendukung nomor telepon tetap 7 atau 8 digit dengan pemisah spasi atau tanda hubung (-). Kelompokkan 7 digit sebagai 3-4 dan 8 digit sebagai 4-4.

493 0286

四九三 零二八六

493-0286

四九三 零二八六

62552560

六二五五 二五六零

6255 2560

六二五五 二五六零

6255-2560

六二五五 二五六零

Nomor telepon tetap+Nomor ekstensi

4930286-109

四九三 零二八六 转幺零九

Mendukung nomor ekstensi 1 hingga 4 digit.

4930286转109

四九三 零二八六 转幺零九

4930286分机109

四九三 零二八六 分机幺零九

4930286分机号109

四九三 零二八六 分机号幺零九

Kode area+Nomor telepon tetap

01062552560

零幺零 六二五五 二五六零

Kode area: 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

三幺九 八九零 七零九八

Kode area+Nomor telepon tetap+Nomor ekstensi

010 62552560-109

零幺零 六二五五 二五六零 转幺零九

Tidak ada

010-62552560-109

零幺零 六二五五 二五六零 转幺零九

(010)62552560-109

零幺零 六二五五 二五六零 转幺零九

(010)62552560转109

零幺零 六二五五 二五六零 转幺零九

(010)62552560分机109

零幺零 六二五五 二五六零 分机幺零九

(010)62552560分机号109

零幺零 六二五五 二五六零 分机号幺零九

Kode negara+Kode area+Nomor telepon tetap

86-010-62791627

八六 零幺零 六二七九 幺六二七

Kode negara 86, (86), +86, (+86), dan 0086 semuanya dibaca “八六” (delapan enam).

(86)10-62791627

八六 幺零 六二七九 幺六二七

+86-010-62791627

八六 零幺零 六二七九 幺六二七

0086-10-62791627

八六 幺零 六二七九 幺六二七

(+86)-10-6279 1627

八六 幺零 六二七九 幺六二七

Kode negara+Kode area+Nomor telepon tetap+Nomor ekstensi

(86)21-58118818-207

八六 二幺 五八幺幺 八八幺八 转二零七

Tidak ada

(86)021-5811-8818-207

八六 零二幺 五八幺幺 八八幺八 转二零七

(86)021-58118818转207

八六 零二幺 五八幺幺 八八幺八 转二零七

(86)21-5811-8818分机207

八六 二幺 五八幺幺 八八幺八 分机二零七

+86-021-58118818分机号207

八六 零二幺 五八幺幺 八八幺八分机号二零七

Nomor ponsel

139 0000 5678

幺三九 零零零零 五六七八

Mendukung nomor ponsel 11 digit dengan pengelompokan 3-3-5 atau 3-4-4.

139-000-05678

幺三九 零零零 零五六七八

139 000 05678

幺三九 零零零 零五六七八

Kode negara+Nomor ponsel

+86-13900005678

八六 幺三九 零零零零 五六七八

Tidak ada

(+86)-139-0000-5678

八六 幺三九 零零零零 五六七八

+8613900005678

八六 幺三九 零零零零 五六七八

0086-139 000 05678

八六 幺三九 零零零 零五六七八

Nomor layanan

123

幺二三

Mendukung nomor layanan umum, nomor 10 digit berawalan 400/800 yang dikelompokkan sebagai 3-3-4, dan nomor 16 digit berawalan 12530/17951/12593.

95678

九五六七八

4008110510

四零零 八幺幺 零五幺零

800-810-8888

八零零 八幺零 八八八八

1253013520638377

幺二五三零 幺三五 二零六三 八三七七

Lainnya

(86)(21)9899-80800-0909

八六 二幺 九八九九 八零八零零 零九零九

Mendukung string digit dengan pemisah (tanda kurung atau tanda hubung).

Inggris

Format

Contoh

Output Inggris

Catatan

String digit

12034

one two oh three four

Tidak ada batas spesifik panjang string digit, tetapi disarankan 20 digit atau kurang.Saat string digit dikelompokkan dengan spasi atau tanda hubung, jeda koma disisipkan antar grup. Maksimal 5 grup didukung.

String digit + spasi/tanda hubung + string digit + spasi/tanda hubung + string digit

1-23-456 7890

one, two three, four five six, seven eight nine oh

Tanda plus + string digit + spasi/tanda hubung + string digit

+43-211-0567

plus four three, two one, oh five six seven

Tanda kurung kiri + string digit + tanda kurung kanan + spasi + string digit + spasi/tanda hubung + string digit

(21) 654-3210

(two one) six five four, three two one oh

address

Mandarin

Format

Contoh

Output Mandarin

Catatan

Format alamat umum

元和镇嘉元30-9

元和镇嘉元三十杠九

Mendukung format alamat umum. Alamat di sini berarti alamat pos standar.

市台路388弄1107-1108号

市台路三八八弄幺幺零七杠幺幺零八号

华润二十四城六期锦云府3-1-3205

华润二十四城六期锦云府三杠一杠三二零五

圣华名都大厦2幢2006室

圣华名都大厦二幢二零零六室

五常街道庭院5幢4单元201

五常街道庭院五幢四单元二零幺

芙蓉江路150弄19号

芙蓉江路幺五零弄十九号

Inggris

Jenis ini tidak didukung untuk teks bahasa Inggris.

id

Mandarin

Format

Contoh

Output Mandarin

Catatan

String

dell0101

D E L L 零 一 零 一

Mendukung huruf Inggris besar dan kecil, digit 0-9, serta garis bawah. Spasi pada output menunjukkan jeda saat karakter dibaca satu per satu.

myid_1998

M Y I D 下划线 一 九 九 八

AiTest

A I T E S T

Inggris

Untuk teks bahasa Inggris, jenis ini bekerja sama seperti characters.

characters

Mandarin

Format

Contoh

Output Mandarin

Catatan

String

ISBN 1-001-099098-1

I S B N 一 杠 零 零 一 杠 零 九 九 零 九 八 杠 一

Mendukung karakter Mandarin, huruf Inggris besar dan kecil, digit 0-9, serta sebagian karakter lebar penuh dan setengah lebar. Spasi menunjukkan jeda antar karakter yang dibaca satu per satu. Escape karakter khusus XML.

x10b2345_u

x 一 零 b 二 三 四 五 下划线 u

v1.0.1

v 一 点 零 点 一

版本号2.0

版本号二 点 零

苏M MA000

苏M M A 零 零 零

空中客车A330

空中客车A 三 三 零

型号s01 s02和s03

型号s 零 一 s 零二 和s 零 三

空中客车A330

空中客车A 三 三 零

αβγ

阿尔法 贝塔 伽玛

Inggris

Format

Contoh

Output Inggris

Catatan

String

*b+3$.c-0'=α

asterisk B plus three dollar dot C dash zero apostrophe equals alpha

Mendukung karakter Tionghoa, huruf besar dan kecil, digit 0-9, dan beberapa karakter full-width dan half-width.

Spasi dalam output menunjukkan jeda antar karakter, artinya setiap karakter dibaca secara individual.

Jika teks di dalam tag berisi karakter khusus XML, escape karakter tersebut.

punctuation

Mandarin

Format

Contoh

Output Mandarin

Catatan

Tanda baca

…

省略号

Mendukung tanda baca Mandarin dan Inggris yang umum. Spasi menunjukkan jeda antar karakter yang dibaca satu per satu. Escape karakter khusus XML.

……

省略号

!"#$%&

叹号 双引号 井号 dollar 百分号 and

‘()*+

单引号 左括号 右括号 星号 加号

,-./:;

逗号 杠 点 斜杠 冒号 分号

<=>?@

小于 等号 大于 问号 at

[\]^_

左方括号 反斜线 右方括号 脱字符 下划线

Inggris

Untuk teks bahasa Inggris, jenis ini bekerja sama seperti characters.

date

Mandarin

Format

Contoh

Output Mandarin

Catatan

xx年

71年

七一年

Mendukung tahun 2 digit dalam rentang 60-99, 00-09, dan 10-19, serta tahun 4 digit dalam rentang 1000-1999 dan 2000-2099.

04年

零四年

19年

一九年

1011年

一零一一年

1998年

一九九八年

2008年

二零零八年

xx年xx月

98年4月

九八年四月

Bulan 1-9 dapat menggunakan atau menghilangkan nol di depan, misalnya “1908年4月” dan “1908年04月”.

1998年04月

一九九八年四月

08年8月

零八年八月

2008年8月

二零零八年八月

xx年xx月xx日xx年xx月xx号

98年4月23日

九八年四月二十三日

Hari 1-9 dapat menggunakan atau menghilangkan nol di depan, misalnya “1908年4月8日” dan “1908年04月08日”.

1998年04月23日

一九九八年四月二十三日

08年8月8号

零八年八月八号

2008年08月08号

二零零八年八月八号

xx年xx月xx日xx年xx月xx号

98年4月23日

九八年四月二十三日

Hari 1-9 dapat menggunakan atau menghilangkan nol di depan, misalnya “1908年4月8日” dan “1908年04月08日”.

1998年04月23日

一九九八年四月二十三日

08年8月8号

零八年八月八号

2008年08月08号

二零零八年八月八号

xx月xx号

3月20日

三月二十日

Tidak ada

08月07号

八月七号

Singkatan tahun/bulan

2018/08

二零一八年八月

Mendukung /, -, dan . sebagai pemisah.

2018-08

二零一八年八月

2018.08

二零一八年八月

Singkatan tahun/bulan/hari

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日

零四年九月一日至三十日

Mendukung ~ dan - untuk menunjukkan “hingga”.

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号

十月一号至七号

Singkatan tahun/bulan/hari~Singkatan tahun/bulan/hari

2018/03/03~2019/01/01

二零一八年三月三日至二零一九年一月一日

Mendukung / dan . sebagai pemisah, serta ~ dan - untuk menunjukkan “hingga”.

1997.9.9~1998.9.9

一九九七年九月九日至一九九八年九月九日

Singkatan bulan/hari~Singkatan bulan/hari

10/20~10/31

十月二十日至十月三十一日

xx~xx月xx月~xx月

1~10月

一至十月

1月~10月

一月至十月

Singkatan bulan/hari/tahun

10/20/2018

二零一八年十月二十日

Hanya mendukung tahun 4 digit, pemisah /, dan urutan bulan/hari/tahun.

Inggris

Format

Contoh

Output Inggris

Catatan

Empat digit/dua digit atau empat digit-dua digit

2000/01

two thousand, oh one

Mencakup beberapa tahun.

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

Bilangan empat digit diawali 1 atau 2

2000

two thousand

Tahun 4 digit.

1900

nineteen hundred

1905

nineteen oh five

2021

twenty one

Hari - hari dalam minggu (tanda hubung)

atau

Hari - hari dalam minggu

atau

Hari & hari dalam minggu

mon-wed

monday to wednesday

Jika teks dalam tag rentang hari dalam minggu berisi karakter XML khusus, escape karakter tersebut.

tue~fri

tuesday to friday

sat&sun

saturday and sunday

DD-DD MMM, YYYY

atau

DD~DD MMM, YYYY

atau

DD&DD MMM, YYYY

19-20 Jan, 2000

the nineteen to the twentieth of january two thousand

DD: Hari 2 digit. MMM: Singkatan bulan 3 huruf atau kata lengkap. YYYY: Tahun 4 digit diawali 1 atau 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

atau

MMM DD~DD

atau

MMM DD&DD

Feb 01 - 03

feburary the first to the third

MMM: Singkatan bulan 3 huruf atau kata lengkap. DD: Hari 2 digit.

Aug 10~20

august the tenth to the twentieth

Dec 11&12

december the eleventh and the twelfth

MMM-MMM

atau

MMM~MMM

atau

MMM&MMM

Jan-Jun

january to june

MMM: Singkatan bulan 3 huruf atau kata lengkap.

jul ~ dec

july to december

sep&oct

september and october

YYYY-YYYY

atau

YYYY~YYYY

1990 - 2000

nineteen ninety to two thousand

YYYY: Tahun 4 digit diawali 1 atau 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 adalah singkatan 3 huruf atau nama lengkap hari dalam minggu. DD adalah hari 2 digit. MMM adalah singkatan 3 huruf atau nama lengkap bulan. MM adalah bulan 2 digit (atau singkatan 3 huruf atau nama lengkap bulan). YYYY adalah tahun 4 digit diawali 1 atau 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

Pembacaan default lainnya

10 Mar, 2001

the tenth of march two thousand one

Tidak ada

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

Mandarin

Format

Contoh

Output Mandarin

Catatan

Waktu

12:00

十二点

Mendukung format waktu dan rentang waktu umum.

12:00:00点

十二点

10:20分

十点二十分

10:20:30

十点二十分三十秒

09:18:14

九点十八分十四秒

Waktu~Waktu

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

上午十点二十分到下午十三点三十分

Singkatan waktu

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

中午十二点二十分十二秒

Inggris

Format

Contoh

Output Inggris

Catatan

HH:MM AM atau PM

09:00 AM

nine A M

HH: Jam 1 atau 2 digit. MM: Menit 2 digit. AM/PM: pagi/sore.

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

Titik waktu-Titik waktu

8:00 am - 05:30 pm

eight a m to five p m

Mendukung format waktu umum dan rentang.

7:05~10:15 AM

seven oh five to ten fifteen A M

09:00-13:00

nine oclock to thirteen hundred

currency

Mandarin

Format

Contoh

Output Mandarin

Catatan

Angka+Penanda mata uang

12.00 RMB

十二人民币

Mendukung AUD (dolar Australia), CAD (dolar Kanada), HKD (dolar Hong Kong), JPY (yen Jepang), USD (dolar AS), CHF (franc Swiss), NOK (krone Norwegia), SEK (krona Swedia), GBP (pound Inggris), RMB (renminbi), CNY (yuan), dan EUR (euro). Angka dapat berupa bilangan bulat, desimal, atau notasi internasional dengan pemisah koma.

12.50 RMB

十二点五零人民币

12,000,000 RMB

一千二百万人民币

12,000,000.00 RMB

一千二百万人民币

12,000.35 RMB

一万两千点三五人民币

Penanda mata uang+Angka

$12

十二美元

Mendukung CAD (dolar Kanada), $ (dolar AS), Fr (franc), kr (krone Denmark), £ (pound Inggris), ¥ (yuan), dan € (euro). Angka dapat berupa bilangan bulat, desimal, atau notasi internasional dengan pemisah koma.

$12.00

十二美元

$12.12

二点一二美元

$12,000

一万两千美元

$12,000.00

一万两千美元

$12,000.99

一万两千点九九美元

Pembacaan default lainnya

1213

一千二百一十三

Tidak ada

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

一万两千

Inggris

Format

Contoh

Output Inggris

Catatan

Angka + identifikasi mata uang

1.00 RMB

one yuan

Format angka yang didukung: bilangan bulat, desimal, dan notasi internasional dipisahkan koma.

Identifikasi mata uang yang didukung:

CN¥ (yuan)

CNY (yuan)

RMB (yuan)

AUD (dolar Australia)

CAD (dolar Kanada)

CHF (franc Swiss)

DKK (krone Denmark)

EUR (euro)

GBP (pound sterling Inggris)

HKD (dolar Hong Kong(China))

JPY (yen Jepang)

NOK (krone Norwegia)

SEK (krona Swedia)

SGD (dolar Singapura)

USD (dolar Amerika Serikat)

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

Identifikasi mata uang + angka

US$ 1.00

one US dollar

Format angka yang didukung: bilangan bulat, desimal, dan notasi internasional dipisahkan koma.

Identifikasi mata uang yang didukung:

US$ (dolar AS)

CA$ (dolar Kanada)

AU$ (dolar Australia)

SG$ (dolar Singapura)

HK$ (dolar Hong Kong)

C$ (dolar Kanada)

A$ (dolar Australia)

$ (dolar)

£ (pound)

€ (euro)

CN¥ (yuan)

CNY (yuan)

RMB (yuan)

AUD (dolar Australia)

CAD (dolar Kanada)

CHF (franc Swiss)

DKK (krone Denmark)

EUR (euro)

GBP (pound sterling Inggris)

HKD (dolar Hong Kong(China))

JPY (yen Jepang)

NOK (krone Norwegia)

SEK (krona Swedia)

SGD (dolar Singapura)

USD (dolar Amerika Serikat)

$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

Angka + klasifikasi + identifikasi mata uang

atau

Identifikasi mata uang + angka+Kuantifier

1.23 Tn RMB

one point two three trillion yuan

Format klasifikasi yang didukung:

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

Mandarin

Format

Contoh

Output Mandarin

Catatan

Angka+Satuan Mandarin

2片

两片

Mendukung satuan Mandarin umum dan singkatan satuan.

120公顷

一百二十公顷

100多毫克

一百多毫克

100来米

一百来米

100余人

一百余人

1厘米20毫米

一厘米二十毫米

120.00平方公里

一百二十平方公里

Angka+Singkatan satuan

120.56 cm²

一百二十点五六平方厘米

120 ㎡ 56 cm²

一百二十平方米五十六平方厘米

100 m 12 cm 6 mm

一百米十二厘米六毫米

Rentang

10~15 kg

十至十五千克

10.24~789.82亩

十点二四至七百八十九点八二亩

10米~15米

十米至十五米

10.24 cm~19.08 cm

十点二四厘米至十九点零八厘米

Angka+Satuan+"/"+Satuan

10元/斤

十元每斤

199~299元/件

一百九十九至二百九十九元每件

299.99元/g~399.99元/g

二百九十九点九九元每克至三百九十九点九九元每克

Pembacaan default lainnya

12扎

十二扎

30 rm

三十r m

4万万同胞

四万万同胞

12.897微克

十二点八九七微克

Inggris

Format

Contoh

Output Inggris

Catatan

Angka + satuan ukuran

1.0 kg

one kilogram

Format angka yang didukung: bilangan bulat, desimal, dan notasi internasional dipisahkan koma.

Mendukung singkatan satuan umum.

1,234.01 km

one thousand two hundred thirty four point zero one kilometres.

Satuan ukuran

mm2

square millimetre

Simbol umum

Simbol

Pembacaan Mandarin

Pembacaan Inggris

!

叹号

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

Satuan umum

Format

Kategori

Contoh Mandarin

Contoh Inggris

Singkatan

Panjang

nm(纳米)、μm(微米)、 mm(毫米)、cm(厘米)、m(米)、km(千米)、ft(英尺)、in(英寸)

nm (nanometre), μm (micrometre), mm (millimetre), cm (centimetre), m (metre), km (kilometre), ft (foot), in (inch)

Luas

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)

Berat

μg(微克)、mg(毫克)、g(克)、kg(千克)

μg (microgram), mg (microgram), g (gram), kg (kilogram)

Waktu

min(分)、sec(秒)、ms(毫秒)

min (minute), sec (second), ms (millisecond)

Elektromagnetisme

μ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)

Suara

dB(分贝)

dB (decibel)

Tekanan

Pa(帕)、kPa(千帕)、Mpa(兆帕)

Pa (pascal), kPa (kilopascal), MPa (megapascal)

Satuan umum lainnya

Juga mendukung satuan Mandarin di luar kategori ini, seperti “米”, “秒”, “美元”, dan “毫升每瓶”, serta kata penggolong Mandarin seperti “架”, “场”, “头”, “部”, dan “盆”.

Juga mendukung satuan lain, seperti tsp (teaspoon), rpm (round per minute), KB (kilobyte), dan mmHg (millimetre of mercury).

Contoh

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>

Hubungan tag

Tag <say-as> dapat berisi teks.

Tag dan simbol LaTeX

Pilih kategori untuk melihat tag dan simbol yang didukung. Contoh teks input menyertakan pembatas. Contoh pembacaan tetap dalam bahasa Mandarin karena fitur ini menghasilkan ucapan bahasa Mandarin.

Matematika dasar

Tag atau simbol

Makna

Contoh sumber rumus

Contoh teks input

Contoh pembacaan

+

Penjumlahan

2 + 3 = 5

$2 + 3 = 5$

二加三等于五

-

Pengurangan

3 - 2 = 1

$3 - 2 = 1$

三减二等于一

\pm

Plus atau minus

\pm 1 \pm 2

$\pm 1\pm 2$

正负一加减二

Positif atau negatif

\times

Perkalian

2 \times 3 = 6

$2 \times 3 = 6$

二乘三等于六

×

2 × 3 = 6

$$2 × 3 = 6$$

*

2 * 3 = 6

\(2 * 3 = 6\)

\div

Pembagian

6\div2=3

\[6\div2=3\]

六除二等于三

÷

6÷2=3

$6÷2=3$

/

6/2=3

$6/2=3$

=

Sama dengan

3+5=8

$3+5=8$

三加五等于八

<

Kurang dari

1< 2

$1< 2$

一小于二

≤

Kurang dari atau sama dengan

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$

>

Lebih dari

2>1

$2>1$

二大于一

≥

Lebih dari atau sama dengan

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

Pecahan

2\frac3

$\frac {2}{3}$

三分之二

^

Pangkat

2^1

$2^{1}$

二的一次方

\sqrt

Akar

\sqrt{9} = 3

$\sqrt {9} = 3$

根号下九等于三

\sqrt[3]{8} = 2

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

三次根号下八等于二

\%

Persentase

5\%

$5\%$

百分之五

|

Nilai mutlak

∣3∣=3

$|3| =3$

绝对值三的绝对值等于三

\vert

3\vert=3

$\vert 3\vert =3$

绝对值三绝对值等于三

\lg

Logaritma

lg {10}

$\lg {10}$

log十

\log

Logaritma

\log{5}

$\log{5}$

log五

\ln

Logaritma natural

\lnX

$ln {10}$

LN十

!

Faktorial

5!

$5!$

五的阶乘

()

Tanda kurung

(2+1)

$(2+1)$

括号内二加一

\{ \}

$\{2+1\}$

大括号二加一反大括号

Simbol matematika khusus

Tag atau simbol

Makna

Contoh sumber rumus

Contoh teks input

Contoh pembacaan

\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

Tak hingga (Mandarin)

\infty

$\infty$

无穷大

∞

infty (Inggris)

∞

$∞$

Geometri

Tag atau simbol

Makna

Contoh sumber rumus

Contoh teks input

Contoh pembacaan

\pi

Pi

\pi=3.14159

$\pi =3.14159$

派等于三点一四一五九

\sin (sine)

Fungsi trigonometri

\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

Sudut

\angle AB

$\angle AB$

角A B

∠

∠AB

$∠AB$

^\circ

Derajat

∠AB = 30^\circ

$∠AB = 30^\circ$

角A B 等于三十度

\odot

Lingkaran

\odot

$\odot$

圆

\overset\frown

Busur

\overset\frown {BC}

$\overset\frown {BC}$

弧BC

\rm{Rt}

Sudut siku-siku

\because \rm{Rt}\triangle ABC

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

因为直角三角形ABC

\mathrm{Rt}

\therefore AB \perp BC

$\therefore AB \perp BC$

所以AB垂直于BC

\triangle

Segitiga

\triangleABC

$\triangle ABC$

三角形ABC

△

△ABC

$△ABC$

\parallelogram

Jajar genjang

\parallelogramABCD

$\parallelogram ABCD$

平行四边形ABCD

\perp

Tegak lurus

AB \perp BC

$AB \perp BC$

A B 垂直于 B C

\bot

AB \bot BC

$AB \bot BC$

⊥

AB ⊥ BC

$AB ⊥ BC$

\parallel

Sejajar

A\parallel B

$A\parallel B$

A平行于B

\equalparallel

Sejajar dan sama

A\equalparallel B

$A\equalparallel B$

A平行且相等于B

\cong

Kongruen

△ABC\cong△DEF

$△ABC\cong△DEF$

三角形ABC全等于三角形DEF

Kondisi

Tag atau simbol

Makna

Contoh sumber rumus

Contoh teks input

Contoh pembacaan

\implies

Mengimplikasikan

\implies 1+1=2

$\implies 1+1=2$

推出一加一等于二

\iff

Ekuivalen

p\iffq

$p\iffq$

p等价q

\because

Karena

\because a = b \therefore b=a

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

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

\therefore

Oleh karena itu

Satuan

Apit satuan dengan \unit, \quantity, \mathit, \mathrm, atau \rm, misalnya \unit{cm}.

Tag atau simbol

Makna

Contoh sumber rumus

Contoh teks input

Contoh pembacaan

mm

Milimeter

5\quantity{mm}

$5\quantity{mm}$

五毫米

cm

Sentimeter

5\quantity{cm}

$5\quantity{cm}$

五厘米

dm

Desimeter

5\quantity{dm}

$5\quantity{dm}$

五分米

m

Meter

5\quantity{m}

$5\quantity{m}$

五米

km

Kilometer

5\quantity{km}

$5\quantity{km}$

五千米

g

Gram

5\quantity{g}

$5\quantity{g}$

五克

kg

Kilogram

5\quantity{kg}

$5\quantity{kg}$

五千克

t

Ton

5\quantity{t}

$5\quantity{t}$

五吨

mm^2

Milimeter persegi

5\quantity{mm^2}

$5\quantity{mm^2}$

五平方毫米

cm^2

Sentimeter persegi

5\quantity{cm^2}

$5\quantity{cm^2}$

五平方厘米

dm^2

Desimeter persegi

5\quantity{dm^2}

$5\quantity{dm^2}$

五平方分米

m^2

Meter persegi

5\quantity{m^2}

$5\quantity{m^2}$

五平方米

km^2

Kilometer persegi

5\quantity{km^2}

$5\quantity{km^2}$

五平方千米

mm^3

Milimeter kubik

5\quantity{mm^3}

$5\quantity{mm^3}$

五立方毫米

cm^3

Sentimeter kubik

5\quantity{cm^3}

$5\quantity{cm^3}$

五立方厘米

dm^3

Desimeter kubik

5\quantity{dm^3}

$5\quantity{dm^3}$

五立方分米

m^3

Meter kubik

5\quantity{m^3}

$5\quantity{m^3}$

五立方米

km^3

Kilometer kubik

5\quantity{km^3}

$5\quantity{km^3}$

五立方千米

ml

Mililiter

5\quantity{ml}

$5\quantity{ml}$

五毫升

s

Detik

5\quantity{s}

$5\quantity{s}$

五秒

min

Menit

5\quantity{min}

$5\quantity{min}$

五分

h

Jam

5\quantity{h}

$5\quantity{h}$

五小时

km/h

Kilometer per jam

5\quantity{km/h}

$5\quantity{km/h}$

五千米每小时

g/l

Gram per liter

5\quantity{g/l}

$5\quantity{g/l}$

五克每升

FAQ

Mengapa rumus tidak dibaca dengan benar?

  1. Periksa apakah rumus diapit oleh $...$, $$...$$, \(...\), atau \[...\].
  2. Periksa tag atau simbol yang tidak didukung.
  3. Pastikan setiap \ di-escape sebagai \\ dalam string permintaan.

Bagaimana menangani garis miring terbalik dalam kode?

Lihat petunjuk escape di Penggunaan. Sumber rumus dan string kode menggunakan notasi berbeda: tulis \frac sebagai \\frac dalam string biasa.