Bahasa Markup Sintesis Ucapan (Speech Synthesis Markup Language/SSML) adalah bahasa markup berbasis XML untuk sintesis suara. SSML memungkinkan model sintesis suara skala besar memproses teks yang kaya dan memberikan kontrol detail halus atas fitur ucapan, seperti laju ucapan, pitch, jeda, dan volume. Anda juga dapat menambahkan musik latar untuk menciptakan efek ucapan yang lebih ekspresif. Topik ini menjelaskan fitur SSML dari CosyVoice dan cara menggunakannya.
Untuk menggunakan model di wilayah China (Beijing), buka halaman Kunci API untuk wilayah China (Beijing)
Batasan
Model: SSML hanya mendukung model cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2.
Voice: SSML hanya mendukung voice yang dikloning dan voice sistem yang ditandai dalam Daftar Voice sebagai voice yang mendukung SSML.
API: SSML hanya mendukung beberapa API.
Java SDK (versi 2.20.3 atau lebih baru): SSML hanya didukung untuk panggilan non-streaming dan streaming unidirectional. Untuk informasi selengkapnya, lihat Dukungan SSML - Java SDK.
Python SDK (versi 1.23.4 atau lebih baru): SSML hanya didukung untuk panggilan non-streaming dan streaming unidirectional. Untuk informasi selengkapnya, lihat Dukungan SSML - Python SDK.
WebSocket API: Saat mengirim instruksi run-task, atur parameter
enable_ssmlketruedan kirim instruksi continue-task hanya sekali. Untuk informasi selengkapnya, lihat Dukungan SSML - WebSocket API.
Mulai
Sebelum menjalankan kode, selesaikan langkah-langkah berikut:
Instal SDK (jika Anda berencana menjalankan contoh Java/Python SDK)
Java SDK
Panggilan non-streaming
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* Catatan fitur SSML:
* 1. SSML hanya didukung untuk panggilan non-streaming dan streaming unidirectional.
* 2. SSML hanya didukung untuk voice yang dikloning dari model cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2,
* serta voice sistem yang ditandai sebagai mendukung SSML dalam daftar voice (misalnya, voice longanyang untuk cosyvoice-v3-flash).
*/
public class Main {
private static String model = "cosyvoice-v3-flash";
private static String voice = "longanyang";
public static void main(String[] args) {
streamAudioDataToSpeaker();
System.exit(0);
}
public static void streamAudioDataToSpeaker() {
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan: .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
// Escape karakter khusus
audio = synthesizer.call("<speak rate=\"2\">My speech rate is faster than normal.</speak>");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// Tutup koneksi WebSocket setelah tugas selesai
synthesizer.getDuplexApi().close(1000, "bye");
}
if (audio != null) {
// Simpan data audio ke file lokal bernama "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);
}
}
// Latensi paket pertama mencakup waktu yang diperlukan untuk membuat koneksi WebSocket
System.out.println(
"[Metric] Request ID: "
+ synthesizer.getLastRequestId()
+ ", First packet latency (ms): "
+ synthesizer.getFirstPackageDelay());
}
}Panggilan streaming unidirectional
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 java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
/**
* Catatan fitur SSML:
* 1. SSML hanya didukung untuk panggilan non-streaming dan streaming unidirectional.
* 2. SSML hanya didukung untuk voice yang dikloning dari model cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2,
* serta voice sistem yang ditandai sebagai mendukung SSML dalam daftar voice (misalnya, voice longanyang untuk cosyvoice-v3-flash).
*/
public class Main {
private static String model = "cosyvoice-v3-flash";
private static String voice = "longanyang";
public static void main(String[] args) {
streamAudioDataToSpeaker();
System.out.println("Audio saved to 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("Failed to create output file: " + 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("Failed to write audio data: " + e.getMessage());
}
}
}
@Override
public void onComplete() {
System.out.println("Received Complete; speech synthesis finished");
closeFileOutputStream(fileOutputStream[0]);
latch.countDown();
}
@Override
public void onError(Exception e) {
System.out.println("Error occurred: " + e.toString());
closeFileOutputStream(fileOutputStream[0]);
latch.countDown();
}
};
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan: .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 unidirectional; langsung mengembalikan null (hasil dikirim secara asinkron melalui callback)
// Escape karakter khusus
synthesizer.call("<speak rate=\"2\">My speech rate is faster than normal.</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("Failed to close WebSocket connection: " + e.getMessage());
}
// Pastikan aliran file ditutup
closeFileOutputStream(fileOutputStream[0]);
}
// Latensi paket pertama mencakup waktu yang diperlukan untuk membuat koneksi WebSocket
System.out.println(
"[Metric] Request ID: "
+ synthesizer.getLastRequestId()
+ ", First packet latency (ms): "
+ synthesizer.getFirstPackageDelay());
}
private static void closeFileOutputStream(FileOutputStream fileOutputStream) {
try {
if (fileOutputStream != null) {
fileOutputStream.close();
}
} catch (IOException e) {
System.err.println("Failed to close file stream: " + e.getMessage());
}
}
}Python SDK
Panggilan non-streaming
# coding=utf-8
# Catatan fitur SSML:
# 1. SSML hanya didukung untuk panggilan non-streaming dan streaming unidirectional.
# 2. SSML hanya didukung untuk voice yang dikloning dari model cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2,
# serta voice sistem yang ditandai sebagai mendukung SSML dalam daftar voice (misalnya, voice longanyang untuk cosyvoice-v3-flash)
import dashscope
from dashscope.audio.tts_v2 import *
import os
# Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# Model
model = "cosyvoice-v3-flash"
# Voice
voice = "longanyang"
# Buat instance SpeechSynthesizer dan teruskan model, voice, dan parameter permintaan lainnya ke konstruktor
synthesizer = SpeechSynthesizer(model=model, voice=voice)
# Panggilan non-streaming; diblokir hingga audio dikembalikan
# Escape karakter khusus
audio = synthesizer.call("<speak rate=\"2\">My speech rate is faster than normal.</speak>")
# Simpan audio secara lokal
with open('output.mp3', 'wb') as f:
f.write(audio)
# Latensi paket pertama mencakup waktu yang diperlukan untuk membuat koneksi WebSocket
print('[Metric] Request ID: {}, First packet latency: {} ms'.format(
synthesizer.get_last_request_id(),
synthesizer.get_first_package_delay()))Panggilan streaming unidirectional
# coding=utf-8
# Catatan fitur SSML:
# 1. SSML hanya didukung untuk panggilan non-streaming dan streaming unidirectional.
# 2. SSML hanya didukung untuk voice yang dikloning dari model cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2,
# serta voice sistem yang ditandai sebagai mendukung SSML dalam daftar voice (misalnya, voice longanyang untuk cosyvoice-v3-flash)
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
# Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# 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("Connection established: " + get_timestamp())
def on_complete(self):
print("Speech synthesis completed; all results received: " + get_timestamp())
if hasattr(self, 'file') and self.file:
self.file.close()
self
# Latensi paket pertama mencakup waktu yang diperlukan untuk membuat koneksi WebSocket
print('[Metric] Request ID: {}, First packet latency: {} ms'.format(
self.synthesizer.get_last_request_id(),
self.synthesizer.get_first_package_delay()))
def on_error(self, message: str):
print(f"Speech synthesis error: {message}")
if hasattr(self, 'file') and self.file:
self.file.close()
def on_close(self):
print("Connection closed: " + get_timestamp())
if hasattr(self, 'file') and self.file:
self.file.close()
def on_event(self, message):
pass
def on_data(self, data: bytes) -> None:
print(get_timestamp() + " Binary audio length: " + str(len(data)))
# Tulis data audio ke file
self.file.write(data)
callback = Callback()
# Buat instance SpeechSynthesizer dan teruskan model, voice, dan parameter permintaan lainnya ke konstruktor
synthesizer = SpeechSynthesizer(
model=model,
voice=voice,
callback=callback,
)
# Tetapkan instance synthesizer ke callback untuk digunakan dalam on_complete
callback.synthesizer = synthesizer
# Panggilan streaming unidirectional; kirim teks untuk disintesis dan terima audio biner secara real-time melalui metode on_data dari callback
# Escape karakter khusus
synthesizer.call("<speak rate=\"2\">My speech rate is faster than normal.</speak>")WebSocket API
Go
// Catatan fitur SSML:
// 1. Atur parameter enable_ssml ke true dalam perintah run-task untuk mengaktifkan SSML.
// 2. Kirim teks SSML menggunakan perintah continue-task; Anda hanya dapat mengirim perintah ini sekali.
// 3. SSML hanya didukung untuk voice yang dikloning dari model cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2,
// serta voice sistem yang ditandai sebagai mendukung SSML dalam daftar voice (misalnya, voice longanyang untuk cosyvoice-v3-flash)
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
"time"
"github.com/google/uuid"
"github.com/gorilla/websocket"
)
const (
wsURL = "wss://dashscope.aliyuncs.com/api-ws/v1/inference/"
outputFile = "output.mp3"
)
func main() {
// Jika Anda belum menyetel kunci API sebagai variabel lingkungan, ganti baris berikut dengan: apiKey := "your_api_key".
// Jangan hard-code kunci API Anda langsung dalam kode produksi untuk mengurangi risiko eksposur.
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("Connection failed. HTTP status code: %d\n", resp.StatusCode)
}
fmt.Println("Connection failed:", err)
return
}
defer conn.Close()
// Hasilkan ID tugas
taskID := uuid.New().String()
fmt.Printf("Generated task ID: %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, Anda hanya dapat mengirim perintah continue-task sekali.
// Jika tidak, Anda akan mendapatkan error "Text request limit violated, expected 1."
"enable_ssml": true,
},
"input": map[string]interface{}{},
},
}
runTaskJSON, _ := json.Marshal(runTaskCmd)
fmt.Printf("Sending run-task command: %s\n", string(runTaskJSON))
err = conn.WriteMessage(websocket.TextMessage, runTaskJSON)
if err != nil {
fmt.Println("Failed to send run-task:", err)
return
}
textSent := false
// Proses pesan
for {
messageType, message, err := conn.ReadMessage()
if err != nil {
fmt.Println("Failed to read message:", err)
break
}
// Tangani pesan biner
if messageType == websocket.BinaryMessage {
fmt.Printf("Received binary message, length: %d\n", len(message))
file, _ := os.OpenFile(outputFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
file.Write(message)
file.Close()
continue
}
// Tangani pesan teks
messageStr := string(message)
fmt.Printf("Received text message: %s\n", strings.ReplaceAll(messageStr, "\n", ""))
// Uraikan JSON 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("Event type: %s\n", event)
switch event {
case "task-started":
fmt.Println("=== Received task-started event ===")
if !textSent {
// Kirim perintah continue-task; saat menggunakan SSML, Anda hanya dapat mengirim perintah ini 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{}{
// Escape karakter khusus
"text": "<speak rate=\"2\">My speech rate is faster than normal.</speak>",
},
},
}
continueTaskJSON, _ := json.Marshal(continueTaskCmd)
fmt.Printf("Sending continue-task command: %s\n", string(continueTaskJSON))
err = conn.WriteMessage(websocket.TextMessage, continueTaskJSON)
if err != nil {
fmt.Println("Failed to send continue-task:", err)
return
}
textSent = true
// Tunda pengiriman 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("Sending finish-task command: %s\n", string(finishTaskJSON))
err = conn.WriteMessage(websocket.TextMessage, finishTaskJSON)
if err != nil {
fmt.Println("Failed to send finish-task:", err)
return
}
}
case "task-finished":
fmt.Println("=== Task finished ===")
return
case "task-failed":
fmt.Println("=== Task failed ===")
if header["error_message"] != nil {
fmt.Printf("Error message: %s\n", header["error_message"])
}
return
case "result-generated":
fmt.Println("Received result-generated event")
}
}
}
}
}
}
C#
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
// Catatan fitur SSML:
// 1. Atur parameter enable_ssml ke true dalam perintah run-task untuk mengaktifkan SSML.
// 2. Kirim teks SSML menggunakan perintah continue-task; Anda hanya dapat mengirim perintah ini sekali.
// 3. SSML hanya didukung untuk voice yang dikloning dari model cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2,
// serta voice sistem yang ditandai sebagai mendukung SSML dalam daftar voice (misalnya, voice longanyang untuk cosyvoice-v3-flash)
class Program {
// Jika Anda belum menyetel kunci API sebagai variabel lingkungan, ganti baris berikut dengan: private const string ApiKey="your_api_key".
// Jangan hard-code kunci API Anda langsung dalam kode produksi untuk mengurangi risiko eksposur.
private static readonly string ApiKey = Environment.GetEnvironmentVariable("DASHSCOPE_API_KEY") ?? throw new InvalidOperationException("DASHSCOPE_API_KEY environment variable is not set.");
// URL server WebSocket
private const string WebSocketUrl = "wss://dashscope.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;
// Menunjukkan 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 menerima 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, Anda hanya dapat mengirim perintah ini sekali
// Escape karakter khusus
await SendContinueTaskCommandAsync("<speak rate=\"2\">My speech rate is faster than normal.</speak>");
// Kirim perintah finish-task
await SendFinishTaskCommandAsync(_taskId);
// Tunggu tugas penerimaan selesai
await receiveTask;
Console.WriteLine("Task completed; connection closed.");
} catch (OperationCanceledException) {
Console.WriteLine("Task canceled.");
} catch (Exception ex) {
Console.WriteLine($"Error occurred: {ex.Message}");
} finally {
_cancellationTokenSource.Cancel();
_webSocket.Dispose();
}
}
private static void ClearOutputFile(string filePath) {
if (File.Exists(filePath)) {
File.WriteAllText(filePath, string.Empty);
Console.WriteLine("Output file cleared.");
} else {
Console.WriteLine("Output file does not exist; no need to clear.");
}
}
private static async Task ConnectToWebSocketAsync(string url) {
var uri = new Uri(url);
if (_webSocket.State == WebSocketState.Connecting || _webSocket.State == WebSocketState.Open) {
return;
}
// Setel 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("Successfully connected to WebSocket service.");
} catch (OperationCanceledException) {
Console.WriteLine("WebSocket connection canceled.");
} catch (Exception ex) {
Console.WriteLine($"WebSocket connection failed: {ex.Message}");
throw;
}
}
private static async Task SendRunTaskCommandAsync(string taskId) {
var command = CreateCommand("run-task", taskId, "duplex", new {
task_group = "audio",
task = "tts",
function = "SpeechSynthesizer",
model = "cosyvoice-v3-flash",
parameters = new
{
text_type = "PlainText",
voice = "longanyang",
format = "mp3",
sample_rate = 22050,
volume = 50,
rate = 1,
pitch = 1,
// Jika enable_ssml diatur ke true, Anda hanya dapat mengirim perintah continue-task sekali.
// Jika tidak, Anda akan mendapatkan error "Text request limit violated, expected 1."
enable_ssml = true
},
input = new { }
});
await SendJsonMessageAsync(command);
Console.WriteLine("Sent run-task command.");
}
private static async Task SendContinueTaskCommandAsync(string text) {
if (_taskId == null) {
throw new InvalidOperationException("Task ID not initialized.");
}
var command = CreateCommand("continue-task", _taskId, "duplex", new {
input = new {
text
}
});
await SendJsonMessageAsync(command);
Console.WriteLine("Sent continue-task command.");
}
private static async Task SendFinishTaskCommandAsync(string taskId) {
var command = CreateCommand("finish-task", taskId, "duplex", new {
input = new { }
});
await SendJsonMessageAsync(command);
Console.WriteLine("Sent finish-task command.");
}
private static async Task SendJsonMessageAsync(string message) {
var buffer = Encoding.UTF8.GetBytes(message);
try {
await _webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, _cancellationTokenSource.Token);
} catch (OperationCanceledException) {
Console.WriteLine("Message send canceled.");
}
}
private static async Task ReceiveMessagesAsync() {
while (_webSocket.State == WebSocketState.Open) {
var response = await ReceiveMessageAsync();
if (response != null) {
var eventStr = response.RootElement.GetProperty("header").GetProperty("event").GetString();
switch (eventStr) {
case "task-started":
Console.WriteLine("Task started.");
_taskStartedTcs.TrySetResult(true);
break;
case "task-finished":
Console.WriteLine("Task completed.");
_cancellationTokenSource.Cancel();
break;
case "task-failed":
Console.WriteLine("Task failed: " + response.RootElement.GetProperty("header").GetProperty("error_message").GetString());
_cancellationTokenSource.Cancel();
break;
default:
// Tangani result-generated di sini jika diperlukan
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) {
// Tangani data biner
Console.WriteLine("Received binary data...");
// 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("Message receive canceled.");
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 proyek contoh:
my-php-project/
├── composer.json
├── vendor/
└── index.php
Isi composer.json (sesuaikan versi dependensi 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/"
}
}
}Isi index.php:
<!-- Catatan fitur SSML: -->
<!-- 1. Atur parameter enable_ssml ke true dalam perintah run-task untuk mengaktifkan SSML. -->
<!-- 2. Kirim teks SSML menggunakan perintah continue-task; Anda hanya dapat mengirim perintah ini sekali. -->
<!-- 3. SSML hanya didukung untuk voice yang dikloning dari model cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2,
serta voice sistem yang ditandai sebagai mendukung SSML dalam daftar voice (misalnya, voice longanyang untuk cosyvoice-v3-flash) -->
<?php
require __DIR__ . '/vendor/autoload.php';
use Ratchet\Client\Connector;
use React\EventLoop\Loop;
use React\Socket\Connector as SocketConnector;
// Jika Anda belum menyetel kunci API sebagai variabel lingkungan, ganti baris berikut dengan: $api_key="your_api_key".
// Jangan hard-code kunci API Anda langsung dalam kode produksi untuk mengurangi risiko eksposur.
$api_key = getenv("DASHSCOPE_API_KEY");
$websocket_url = 'wss://dashscope.aliyuncs.com/api-ws/v1/inference/'; // URL 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 "Connected to WebSocket server\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, Anda hanya dapat mengirim perintah ini sekali
$continueTaskMessage = json_encode([
"header" => [
"action" => "continue-task",
"task_id" => $taskId,
"streaming" => "duplex"
],
"payload" => [
"input" => [
// Escape karakter khusus
"text" => "<speak rate=\"2\">My speech rate is faster than normal.</speak>"
]
]
]);
$conn->send($continueTaskMessage);
// Kirim perintah finish-task
sendFinishTaskMessage($conn, $taskId);
};
// Bendera untuk melacak 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 {
// Tangani pesan non-biner
$response = json_decode($msg, true);
if (isset($response['header']['event'])) {
handleEvent($conn, $response, $sendContinueTask, $loop, $taskId, $taskStarted);
} else {
echo "Unknown message format\n";
}
}
});
// Dengarkan penutupan koneksi
$conn->on('close', function($code = null, $reason = null) {
echo "Connection closed\n";
if ($code !== null) {
echo "Close code: " . $code . "\n";
}
if ($reason !== null) {
echo "Close reason: " . $reason . "\n";
}
});
}, function ($e) {
echo "Connection failed: {$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, Anda hanya dapat mengirim perintah continue-task sekali.
// Jika tidak, Anda akan mendapatkan error "Text request limit violated, expected 1."
"enable_ssml" => true
],
"input" => (object) []
]
]);
echo "Preparing to send run-task command: " . $runTaskMessage . "\n";
$conn->send($runTaskMessage);
echo "run-task command sent\n";
}
/**
* Baca file audio
* @param string $filePath
* @return bool|string
*/
function readAudioFile(string $filePath) {
$voiceData = file_get_contents($filePath);
if ($voiceData === false) {
echo "Failed to read audio file\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 "Preparing to send finish-task command: " . $finishTaskMessage . "\n";
$conn->send($finishTaskMessage);
echo "finish-task command sent\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 "Task started; sending continue-task command...\n";
$taskStarted = true;
// Kirim perintah continue-task
$sendContinueTask();
break;
case 'result-generated':
// Abaikan event result-generated
break;
case 'task-finished':
echo "Task completed\n";
$conn->close();
break;
case 'task-failed':
echo "Task failed\n";
echo "Error code: " . $response['header']['error_code'] . "\n";
echo "Error message: " . $response['header']['error_message'] . "\n";
$conn->close();
break;
case 'error':
echo "Error: " . $response['payload']['message'] . "\n";
break;
default:
echo "Unknown event: " . $response['header']['event'] . "\n";
break;
}
// Tutup koneksi setelah tugas selesai
if ($response['header']['event'] == 'task-finished') {
// Tunggu 1 detik untuk memastikan semua data dikirim
$loop->addTimer(1, function() use ($conn) {
$conn->close();
echo "Client closed connection\n";
});
}
// Tutup koneksi jika task-started tidak diterima dan terjadi error
if (!$taskStarted && in_array($response['header']['event'], ['task-failed', 'error'])) {
$conn->close();
}
}Node.js
Instal dependensi:
npm install ws
npm install uuidContoh kode:
// Catatan fitur SSML:
// 1. Atur parameter enable_ssml ke true dalam perintah run-task untuk mengaktifkan SSML.
// 2. Kirim teks SSML menggunakan perintah continue-task; Anda hanya dapat mengirim perintah ini sekali.
// 3. SSML hanya didukung untuk voice yang dikloning dari model cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2,
// serta voice sistem yang ditandai sebagai mendukung SSML dalam daftar voice (misalnya, voice longanyang untuk cosyvoice-v3-flash)
import fs from 'fs';
import WebSocket from 'ws';
import { v4 as uuid } from 'uuid'; // Untuk menghasilkan UUID
// Jika Anda belum menyetel kunci API sebagai variabel lingkungan, ganti baris berikut dengan: apiKey = 'your_api_key'.
// Jangan hard-code kunci API Anda langsung dalam kode produksi untuk mengurangi risiko eksposur.
const apiKey = process.env.DASHSCOPE_API_KEY;
// URL server WebSocket
const url = 'wss://dashscope.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('Connected to WebSocket server');
// 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 // Aktifkan SSML. Jika diatur ke true, Anda hanya dapat mengirim perintah continue-task sekali.
// Jika tidak, Anda akan mendapatkan error "Text request limit violated, expected 1."
},
input: {}
}
});
ws.send(runTaskMessage);
console.log('Sent run-task message');
});
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('Task started');
// Kirim perintah continue-task
sendContinueTasks(ws);
break;
case 'task-finished':
console.log('Task completed');
ws.close();
fileStream.end(() => {
console.log('File stream closed');
});
break;
case 'task-failed':
console.error('Task failed:', message.header.error_message);
ws.close();
fileStream.end(() => {
console.log('File stream closed');
});
break;
default:
// Tangani result-generated di sini jika diperlukan
break;
}
}
});
function sendContinueTasks(ws) {
if (taskStarted) {
// Kirim perintah continue-task; saat menggunakan SSML, Anda hanya dapat mengirim perintah ini sekali
const continueTaskMessage = JSON.stringify({
header: {
action: 'continue-task',
task_id: taskId,
streaming: 'duplex'
},
payload: {
input: {
// Escape karakter khusus
text: '<speak rate="2">My speech rate is faster than normal.</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('Disconnected from WebSocket server');
});Java
Jika Anda menggunakan Java, kami merekomendasikan menggunakan Java DashScope SDK. Untuk informasi selengkapnya, lihat Java SDK.
Berikut adalah contoh panggilan WebSocket Java. Sebelum menjalankan contoh, pastikan Anda telah mengimpor dependensi berikut:
Java-WebSocketjackson-databind
Kami merekomendasikan menggunakan Maven atau Gradle untuk mengelola dependensi. Contoh konfigurasi:
pom.xml
<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>build.gradle
// Omit other code
dependencies {
// WebSocket Client
implementation 'org.java-websocket:Java-WebSocket:1.5.3'
// JSON Processing
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0'
}
// Omit other codeKode 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.*;
/**
* Catatan fitur SSML:
* 1. Atur parameter enable_ssml ke true dalam perintah run-task untuk mengaktifkan SSML.
* 2. Kirim teks SSML menggunakan perintah continue-task; Anda hanya dapat mengirim perintah ini sekali.
* 3. SSML hanya didukung untuk voice yang dikloning dari model cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2,
* serta voice sistem yang ditandai sebagai mendukung SSML dalam daftar voice (misalnya, voice longanyang untuk cosyvoice-v3-flash)
*/
public class TTSWebSocketClient extends WebSocketClient {
private final String taskId = UUID.randomUUID().toString();
private final String outputFile = "output_" + System.currentTimeMillis() + ".mp3";
private boolean taskFinished = false;
public TTSWebSocketClient(URI serverUri, Map<String, String> headers) {
super(serverUri, headers);
}
@Override
public void onOpen(ServerHandshake serverHandshake) {
System.out.println("Connection successful");
// Kirim perintah run-task
// Jika enable_ssml diatur ke true, Anda hanya dapat mengirim perintah continue-task sekali.
// Jika tidak, Anda akan mendapatkan error "Text request limit violated, expected 1."
String runTaskCommand = "{ \"header\": { \"action\": \"run-task\", \"task_id\": \"" + taskId + "\", \"streaming\": \"duplex\" }, \"payload\": { \"task_group\": \"audio\", \"task\": \"tts\", \"function\": \"SpeechSynthesizer\", \"model\": \"cosyvoice-v3-flash\", \"parameters\": { \"text_type\": \"PlainText\", \"voice\": \"longanyang\", \"format\": \"mp3\", \"sample_rate\": 22050, \"volume\": 50, \"rate\": 1, \"pitch\": 1, \"enable_ssml\": true }, \"input\": {} }}";
send(runTaskCommand);
}
@Override
public void onMessage(String message) {
System.out.println("Received message from 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("Received task-started event from server");
// Kirim perintah continue-task; saat menggunakan SSML, Anda hanya dapat mengirim perintah ini sekali
// Escape karakter khusus
sendContinueTask("<speak rate=\\\"2\\\">My speech rate is faster than normal.</speak>");
// Kirim perintah finish-task
sendFinishTask();
} else if ("task-finished".equals(event)) {
System.out.println("Received task-finished event from server");
taskFinished = true;
closeConnection();
} else if ("task-failed".equals(event)) {
System.out.println("Task failed: " + message);
closeConnection();
}
}
}
} catch (Exception e) {
System.err.println("Error occurred: " + e.getMessage());
}
}
@Override
public void onMessage(ByteBuffer message) {
System.out.println("Received binary audio data size: " + message.remaining());
try (FileOutputStream fos = new FileOutputStream(outputFile, true)) {
byte[] buffer = new byte[message.remaining()];
message.get(buffer);
fos.write(buffer);
System.out.println("Audio data written to local file " + outputFile);
} catch (IOException e) {
System.err.println("Failed to write audio data to local file: " + e.getMessage());
}
}
@Override
public void onClose(int code, String reason, boolean remote) {
System.out.println("Connection closed: " + reason + " (" + code + ")");
}
@Override
public void onError(Exception ex) {
System.err.println("Error: " + ex.getMessage());
ex.printStackTrace();
}
private void sendContinueTask(String text) {
String command = "{ \"header\": { \"action\": \"continue-task\", \"task_id\": \"" + taskId + "\", \"streaming\": \"duplex\" }, \"payload\": { \"input\": { \"text\": \"" + text + "\" } }}";
send(command);
}
private void sendFinishTask() {
String command = "{ \"header\": { \"action\": \"finish-task\", \"task_id\": \"" + taskId + "\", \"streaming\": \"duplex\" }, \"payload\": { \"input\": {} }}";
send(command);
}
private void closeConnection() {
if (!isClosed()) {
close();
}
}
public static void main(String[] args) {
try {
String apiKey = System.getenv("DASHSCOPE_API_KEY");
if (apiKey == null || apiKey.isEmpty()) {
System.err.println("Please set the DASHSCOPE_API_KEY environment variable");
return;
}
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "bearer " + apiKey);
TTSWebSocketClient client = new TTSWebSocketClient(new URI("wss://dashscope.aliyuncs.com/api-ws/v1/inference/"), headers);
client.connect();
while (!client.isClosed() && !client.taskFinished) {
Thread.sleep(1000);
}
} catch (Exception e) {
System.err.println("Failed to connect to WebSocket service: " + e.getMessage());
e.printStackTrace();
}
}
}Python
Jika Anda menggunakan Python, kami merekomendasikan menggunakan Python DashScope SDK. Untuk informasi selengkapnya, lihat Python SDK.
Berikut adalah contoh panggilan WebSocket Python. Sebelum menjalankan contoh, instal dependensi sebagai berikut:
pip uninstall websocket-client
pip uninstall websocket
pip install websocket-clientJangan beri nama skrip Python Anda "websocket.py". Jika tidak, Anda akan mengalami AttributeError.
# Catatan fitur SSML:
# 1. Atur parameter enable_ssml ke true dalam perintah run-task untuk mengaktifkan SSML.
# 2. Kirim teks SSML menggunakan perintah continue-task; Anda hanya dapat mengirim perintah ini sekali.
# 3. SSML hanya didukung untuk voice yang dikloning dari model cosyvoice-v3-flash, cosyvoice-v3-plus, dan cosyvoice-v2,
# serta voice sistem yang ditandai sebagai mendukung SSML dalam daftar voice (misalnya, voice longanyang untuk cosyvoice-v3-flash)
import websocket
import json
import uuid
import os
import time
class TTSClient:
def __init__(self, api_key, uri):
"""
Inisialisasi instance TTSClient
Args:
api_key (str): Kunci API untuk autentikasi
uri (str): URL layanan WebSocket
"""
self.api_key = api_key # Ganti dengan kunci API Anda
self.uri = uri # Ganti dengan URL WebSocket Anda
self.task_id = str(uuid.uuid4()) # Hasilkan ID tugas unik
self.output_file = f"output_{int(time.time())}.mp3" # Jalur file audio output
self.ws = None # Instance 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 terbentuk
Mengirim perintah run-task untuk memulai sintesis suara
"""
print("WebSocket connected")
# Buat 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, Anda hanya dapat mengirim perintah continue-task sekali.
# Jika tidak, Anda akan mendapatkan error "Text request limit violated, expected 1."
"enable_ssml": True
},
"input": {}
}
}
# Kirim perintah run-task
ws.send(json.dumps(run_task_cmd))
print("Sent run-task command")
def on_message(self, ws, message):
"""
Callback saat pesan diterima
Menangani pesan teks dan biner
"""
if isinstance(message, str):
# Tangani pesan teks JSON
try:
msg_json = json.loads(message)
print(f"Received JSON message: {msg_json}")
if "header" in msg_json:
header = msg_json["header"]
if "event" in header:
event = header["event"]
if event == "task-started":
print("Task started")
self.task_started = True
# Kirim perintah continue-task; saat menggunakan SSML, Anda hanya dapat mengirim perintah ini sekali
# Escape karakter khusus
self.send_continue_task("<speak rate=\"2\">My speech rate is faster than normal.</speak>")
# Kirim finish-task setelah continue-task
self.send_finish_task()
elif event == "task-finished":
print("Task completed")
self.task_finished = True
self.close(ws)
elif event == "task-failed":
error_msg = msg_json.get("error_message", "Unknown error")
print(f"Task failed: {error_msg}")
self.task_finished = True
self.close(ws)
except json.JSONDecodeError as e:
print(f"JSON parsing failed: {e}")
else:
# Tangani pesan biner (data audio)
print(f"Received binary message, size: {len(message)} bytes")
with open(self.output_file, "ab") as f:
f.write(message)
print(f"Audio data written to local file {self.output_file}")
def on_error(self, ws, error):
"""Callback saat terjadi error"""
print(f"WebSocket error: {error}")
def on_close(self, ws, close_status_code, close_msg):
"""Callback saat koneksi ditutup"""
print(f"WebSocket closed: {close_msg} ({close_status_code})")
def send_continue_task(self, text):
"""Kirim perintah continue-task dengan 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"Sent continue-task command, text: {text}")
def send_finish_task(self):
"""Kirim perintah finish-task untuk mengakhiri sintesis suara"""
cmd = {
"header": {
"action": "finish-task",
"task_id": self.task_id,
"streaming": "duplex"
},
"payload": {
"input": {}
}
}
self.ws.send(json.dumps(cmd))
print("Sent finish-task command")
def close(self, ws):
"""Tutup koneksi secara aktif"""
if ws and ws.sock and ws.sock.connected:
ws.close()
print("Connection closed actively")
def run(self):
"""Mulai klien WebSocket"""
# Setel header permintaan (autentikasi)
header = {
"Authorization": f"bearer {self.api_key}",
"X-DashScope-DataInspection": "enable"
}
# Buat instance 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("Listening for WebSocket messages...")
self.ws.run_forever() // Mulai koneksi jangka panjang
// Contoh penggunaan
if __name__ == "__main__":
API_KEY = os.environ.get("DASHSCOPE_API_KEY") // Jika tidak disetel sebagai variabel lingkungan, setel API_KEY ke kunci API Anda
SERVER_URI = "wss://dashscope.aliyuncs.com/api-ws/v1/inference/"
client = TTSClient(API_KEY, SERVER_URI)
client.run()Tag
Implementasi SSML untuk layanan sintesis suara didasarkan pada spesifikasi W3C SSML 1.0. Namun, untuk mengakomodasi berbagai skenario bisnis, tidak semua tag standar didukung. Sebagai gantinya, kami mendukung kumpulan tag yang paling praktis.
Semua konten teks yang menggunakan fitur SSML harus diapit oleh tag
<speak></speak>.Anda dapat menggunakan beberapa tag
<speak>secara berurutan, seperti<speak></speak><speak></speak>. Anda tidak dapat menumpuknya, seperti<speak><speak></speak></speak>.Anda harus melakukan escape karakter khusus XML dalam konten teks tag. Karakter khusus umum dan bentuk escape-nya adalah sebagai berikut:
"(tanda kutip ganda) →"'(tanda kutip tunggal/apostrof) →'&(ampersand) →&<(tanda kurang dari) → <>(tanda lebih dari) → >
<speak>: Node root
Deskripsi
Tag
<speak>adalah node root untuk semua dokumen SSML. Semua teks yang menggunakan fitur SSML harus diapit oleh tag<speak></speak>.Sintaks
<speak>Teks yang memerlukan fitur SSML</speak>Properti
Properti
Tipe
Wajib
Deskripsi
voice
String
Tidak
Menentukan voice.
Properti ini memiliki prioritas lebih tinggi daripada parameter
voicedalam permintaan API.Nilai valid: Untuk informasi lebih lanjut tentang voice tertentu, lihat voice cosyvoice-v2.
Contoh:
<speak voice="longcheng_v2"> I am a male voice. </speak>
rate
String
Tidak
Menentukan laju ucapan. Properti ini memiliki prioritas lebih tinggi daripada parameter
speech_ratedalam permintaan API.Nilai valid: angka desimal dari 0,5 hingga 2.
Nilai default: 1
Nilai lebih dari 1 menunjukkan laju ucapan lebih cepat.
Nilai kurang dari 1 menunjukkan laju ucapan lebih lambat.
Contoh:
<speak rate="2"> My speech rate is faster than normal. </speak>
pitch
String
Tidak
Menentukan pitch. Properti ini memiliki prioritas lebih tinggi daripada parameter
pitch_ratedalam permintaan API.Nilai valid: angka desimal dari 0,5 hingga 2.
Nilai default: 1
Nilai lebih dari 1 menunjukkan pitch lebih tinggi.
Nilai kurang dari 1 menunjukkan pitch lebih rendah.
Contoh:
<speak pitch="0.5"> However, my pitch is lower than others. </speak>
volume
String
Tidak
Menentukan volume. Properti ini memiliki prioritas lebih tinggi daripada parameter
volumedalam permintaan API.Nilai valid: bilangan bulat dari 0 hingga 100.
Nilai default: 50
Nilai lebih dari 50 menunjukkan volume lebih tinggi.
Nilai kurang dari 50 menunjukkan volume lebih rendah.
Contoh:
<speak volume="80"> My volume is also very high. </speak>
effect
String
Tidak
Menentukan efek suara.
Nilai valid:
robot: efek suara robot
lolita: efek suara perempuan muda ceria
lowpass: efek suara low-pass
echo: efek gema
eq: equalizer (advanced)
lpfilter: filter lolos-rendah (advanced)
hpfilter: filter high-pass (advanced)
Catataneq, lpfilter, dan hpfilter adalah jenis efek suara lanjutan. Anda dapat menggunakan parameter
effectValueuntuk menyesuaikan efek spesifiknya.Setiap tag SSML hanya mendukung satu efek suara. Beberapa atribut
effecttidak dapat berdampingan.Menggunakan efek suara meningkatkan latensi sistem.
Contoh:
<speak effect="robot"> Do you like the robot WALL-E? </speak>
effectValue
String
Tidak
Menentukan efek spesifik dari efek suara (parameter
effect).Nilai valid:
eq(equalizer): Sistem mendukung delapan level frekuensi secara default:["40 Hz", "100 Hz", "200 Hz", "400 Hz", "800 Hz", "1600 Hz", "4000 Hz", "12000 Hz"].
Bandwidth setiap band frekuensi adalah 1,0 q.
Saat Anda menggunakan efek ini, Anda harus menggunakan parameter
effectValueuntuk menentukan nilai gain untuk setiap band frekuensi. Parameter ini adalah string delapan bilangan bulat yang dipisahkan spasi. Nilai setiap bilangan bulat berkisar dari -20 hingga 20. Nilai0menunjukkan bahwa gain frekuensi yang sesuai tidak disesuaikan.Contoh:
effectValue="1 1 1 1 1 1 1 1"lpfilter(filter low-pass): Masukkan nilai frekuensi filter low-pass. Nilainya adalah bilangan bulat dalam rentang (0, laju sampel target/2]. Misalnya, effectValue="800".hpfilter(filter high-pass): Masukkan nilai frekuensi filter high-pass. Nilainya adalah bilangan bulat dalam rentang (0, laju sampel target/2]. Misalnya, effectValue="1200".
Contoh:
<speak effect="eq" effectValue="1 -20 1 1 1 1 20 1"> Do you like the robot WALL-E? </speak> <speak effect="lpfilter" effectValue="1200"> Do you like the robot WALL-E? </speak> <speak effect="hpfilter" effectValue="1200"> Do you like the robot WALL-E? </speak>
bgm
String
Tidak
Menambahkan musik latar yang ditentukan ke ucapan yang disintesis. File musik latar harus disimpan di Alibaba Cloud OSS (lihat Unggah file), dan bucket-nya harus memiliki izin baca-publik minimal.
Jika URL musik latar mengandung karakter khusus XML, seperti
&,<, dan>, Anda harus melakukan escape.Persyaratan audio:
Tidak ada batas atas ukuran file audio, tetapi file yang lebih besar dapat meningkatkan waktu unduh. Jika durasi konten yang disintesis melebihi durasi musik latar, musik latar akan diputar ulang secara otomatis agar sesuai dengan panjang audio yang disintesis.
Laju sampel: 16 kHz
Jumlah saluran suara: mono
Format file: WAV
Jika audio asli tidak dalam format WAV, gunakan tool
ffmpeguntuk mengubahnya:ffmpeg -i input_audio -acodec pcm_s16le -ac 1 -ar 16000 output.wavKedalaman bit: 16-bit
Contoh:
<speak bgm="http://nls.alicdn.com/bgm/2.wav" backgroundMusicVolume="30" rate="-500" volume="40"> <break time="2s"/> The old trees on the shady cliff are shrouded in mist <break time="700ms"/> The sound of rain is still in the bamboo forest <break time="700ms"/> I know that cotton contributes to the country's plan <break time="700ms"/> The scenery of Mianzhou is always pitiable <break time="2s"/> </speak>
PentingAnda bertanggung jawab secara hukum atas hak cipta audio yang diunggah.
backgroundMusicVolume
String
Tidak
Mengontrol volume musik latar. Ini dikonfigurasi menggunakan properti
backgroundMusicVolume.Hubungan tag
Tag <speak> dapat berisi teks dan tag berikut:
Contoh lain
Atribut kosong
<speak> Teks yang memerlukan tag SSML </speak>Kombinasi atribut (dipisahkan spasi)
<speak rate="200" pitch="-100" volume="80"> So when put together, my voice sounds like this. </speak>
<break>: Mengontrol durasi jeda
Deskripsi
Menambahkan periode diam selama sintesis ucapan untuk mensimulasikan jeda alami. Anda dapat mengatur durasi dalam detik (s) atau milidetik (ms). Tag ini opsional.
Sintaks
# Atribut kosong <break/> # Dengan atribut time <break time="string"/>Properti
CatatanJika Anda menggunakan tag <break> tanpa atribut, durasi jeda default adalah 1 s.
Properti
Tipe
Wajib
Deskripsi
time
String
Tidak
Mengatur durasi jeda dalam detik atau milidetik, seperti "2s" atau "50ms".
Nilai valid:
Dalam detik (s): bilangan bulat dari 1 hingga 10.
Dalam milidetik (ms): bilangan bulat dari 50 hingga 10000.
Contoh:
<speak> Please close your eyes and take a rest.<break time="500ms"/>Okay, please open your eyes. </speak>
PentingJika Anda menggunakan beberapa tag
<break>secara berurutan, durasi jeda total adalah jumlah waktu yang ditentukan di setiap tag. Jika durasi total melebihi 10 detik, hanya 10 detik pertama yang berlaku.Misalnya, dalam segmen SSML berikut, durasi kumulatif tag
<break>adalah 15 detik, yang melebihi batas 10 detik. Durasi jeda akhir akan dipotong menjadi 10 detik:<speak> Please close your eyes and take a rest.<break time="5s"/><break time="5s"/><break time="5s"/>Okay, please open your eyes. </speak>Hubungan tag
<break> adalah tag kosong dan tidak dapat berisi tag lain.
<sub>: Mengganti teks
Deskripsi
Mengganti string teks dengan alternatif yang ditentukan yang dibaca keras sebagai gantinya. Misalnya, teks "W3C" dapat dibaca sebagai "protokol jaringan". Tag ini opsional.
Sintaks
<sub alias="string"></sub>Properti
Properti
Tipe
Wajib
Deskripsi
alias
String
Ya
Mengganti sepotong teks dengan teks yang lebih cocok untuk dibaca.
Contoh:
<speak> <sub alias="protokol jaringan">W3C</sub> </speak>Hubungan tag
Tag <sub> hanya dapat berisi teks.
<phoneme>: Menentukan pelafalan (Pinyin/alfabet fonetik)
Deskripsi
Mengontrol pelafalan string teks tertentu. Anda dapat menggunakan Pinyin untuk bahasa Mandarin dan alfabet fonetik, seperti CMU, untuk bahasa Inggris. Tag ini cocok untuk skenario yang memerlukan pelafalan tepat dan bersifat opsional.
Sintaks
<phoneme alphabet="string" ph="string">text</phoneme>Properti
Properti
Tipe
Wajib
Deskripsi
alphabet
String
Ya
Menentukan jenis pelafalan: Pinyin (untuk bahasa Mandarin) atau alfabet fonetik (untuk bahasa Inggris).
Nilai valid:
"py": Pinyin
"cmu": alfabet fonetik. Untuk informasi selengkapnya, lihat The CMU Pronouncing Dictionary.
ph
String
Ya
Menentukan Pinyin atau alfabet fonetik spesifik:
Pinyin untuk setiap karakter dipisahkan spasi, dan jumlah suku kata Pinyin harus sesuai dengan jumlah karakter.
Setiap suku kata Pinyin terdiri dari bagian pelafalan dan nada. Nada adalah bilangan bulat dari
1hingga5, di mana5menunjukkan nada netral.Contoh:
<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.
<soundEvent>: Menyisipkan suara eksternal (seperti nada dering atau suara kucing)
Deskripsi
Memungkinkan Anda menyisipkan file efek suara, seperti nada prompt atau suara latar, ke dalam ucapan yang disintesis untuk memperkaya output audio. Tag ini opsional.
Sintaks
<soundEvent src="URL"/>Properti
Properti
Tipe
Wajib
Deskripsi
src
String
Ya
Mengatur URL audio eksternal.
File audio harus disimpan di OSS (lihat Unggah file), dan bucket-nya harus memiliki izin baca-publik minimal. Jika URL mengandung karakter khusus XML, seperti
&,<, dan>, Anda harus melakukan escape.Persyaratan audio:
Laju sampel: 16 kHz
Jumlah saluran suara: mono
Format file: WAV
Jika audio asli tidak dalam format WAV, gunakan tool
ffmpeguntuk mengubahnya:ffmpeg -i input_audio -acodec pcm_s16le -ac 1 -ar 16000 output.wav
Ukuran file: tidak lebih dari 2 MB
Kedalaman bit: 16-bit
Contoh:
<speak> A horse was frightened<soundEvent src="http://nls.alicdn.com/sound-event/horse-neigh.wav"/>and people scattered to avoid it. </speak>
PentingAnda bertanggung jawab secara hukum atas hak cipta audio yang diunggah.
Hubungan tag
<soundEvent> adalah tag kosong dan tidak dapat berisi tag lain.
<say-as>: Mengatur cara membaca teks (seperti angka, tanggal, dan nomor telepon)
Deskripsi
Menunjukkan jenis konten string teks, yang memungkinkan model membaca teks dalam format yang sesuai. Tag ini opsional.
Sintaks
<say-as interpret-as="string">text</say-as>Properti
Properti
Tipe
Wajib
Deskripsi
interpret-as
String
Ya
Menunjukkan jenis informasi teks dalam tag.
Nilai valid:
cardinal: Dibaca sebagai angka kardinal (bilangan bulat atau desimal).
digits: Dibaca sebagai digit individual. Misalnya, 123 dibaca sebagai satu dua tiga.
telephone: Dibaca sebagai nomor telepon.
name: Dibaca sebagai nama.
address: Dibaca sebagai alamat.
id: Cocok untuk nama akun dan nama panggilan. Dibaca dengan cara konvensional.
characters: Membaca teks dalam tag karakter per karakter.
punctuation: Membaca teks dalam tag sebagai tanda baca.
date: Dibaca sebagai tanggal.
time: Dibaca sebagai waktu.
currency: Dibaca sebagai jumlah mata uang.
measure: Dibaca sebagai satuan ukuran.
Format yang didukung untuk setiap jenis <say-as>
cardinal
Format
Contoh
English output
Deskripsi
String angka
145
one hundred forty five
Rentang input bilangan bulat: bilangan bulat positif atau negatif dalam 13 digit, [-999999999999, 999999999999].
Rentang input desimal: Tidak ada batasan khusus pada jumlah tempat desimal, tetapi disarankan tidak melebihi 10.
String angka diawali nol
0145
one hundred forty five
Tanda minus + string angka
-145
minus hundred forty five
String angka 3 digit dipisahkan koma
60,000
sixty thousand
Tanda minus + string angka 3 digit dipisahkan koma
-208,000
minus two hundred eight thousand
String angka + titik desimal + nol
12.00
twelve
String angka + titik desimal + string angka
12.34
twelve point three four
String angka 3 digit dipisahkan koma + titik desimal + string angka
1,000.1
one thousand point one
Tanda minus + string angka + titik desimal + string angka
-12.34
minus twelve point three four
Tanda minus + string angka 3 digit dipisahkan koma + titik desimal + string angka
-1,000.1
minus one thousand point one
(String angka dipisahkan koma 3 digit) + tanda hubung + (angka dipisahkan koma 3 digit)
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
Format
Contoh
Output Bahasa Inggris
Deskripsi
String angka
12034
one two zero three four
Tidak ada batasan khusus pada panjang string angka, tetapi disarankan tidak melebihi 20 digit.
Saat string angka dikelompokkan dengan spasi atau tanda hubung, koma dimasukkan di antara kelompok untuk membuat jeda yang sesuai. Mendukung hingga 5 kelompok.
String angka + spasi atau tanda hubung + string angka + spasi atau tanda hubung + string angka + spasi atau tanda hubung + string angka
1-23-456 7890
one, two three, four five six, seven eight nine zero
telephone
Format
Contoh
Output Bahasa Inggris
Deskripsi
String angka
12034
one two oh three four
Tidak ada batasan khusus pada panjang string angka, tetapi disarankan tidak melebihi 20 digit. Saat string angka dikelompokkan dengan spasi atau tanda hubung, koma dimasukkan di antara kelompok untuk membuat jeda yang sesuai. Mendukung hingga 5 kelompok.
String angka + spasi atau tanda hubung + string angka + spasi atau tanda hubung + string angka
1-23-456 7890
one, two three, four five six, seven eight nine oh
Tanda plus + string angka + spasi atau tanda hubung + string angka
+43-211-0567
plus four three, two one one, oh five six seven
Tanda kurung kiri + string angka + tanda kurung kanan + spasi + string angka + spasi atau tanda hubung + string angka
(21) 654-3210
(two one) six five four, three two one oh
address
Tag ini tidak didukung untuk teks bahasa Inggris.
id
Untuk teks bahasa Inggris, tag ini berfungsi sama seperti tag characters.
characters
Format
Contoh
English output
Deskripsi
string
*b+3$.c-0'=α
asterisk B plus three dollar dot C dash zero apostrophe equals alpha
Mendukung karakter Mandarin, huruf Inggris besar dan kecil, angka Arab 0-9, dan beberapa karakter lebar penuh dan setengah lebar.
Spasi dalam output menunjukkan bahwa jeda dimasukkan di antara setiap karakter, artinya karakter dibaca satu per satu.
Jika teks dalam tag mengandung karakter khusus XML, Anda harus melakukan escape.
punctuation
Untuk teks bahasa Inggris, tag ini berfungsi sama seperti tag characters.
date
Format
Contoh
Output Bahasa Inggris
Deskripsi
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
Angka empat digit yang diawali 1 atau 2
2000
two thousand
Tahun empat digit.
1900
nineteen hundred
1905
nineteen oh five
2021
twenty twenty one
Hari dalam minggu-Hari dalam minggu
atau
Hari dalam minggu~Hari dalam minggu
atau
Hari dalam minggu&Hari dalam minggu
mon-wed
monday to wednesday
Jika teks dalam tag rentang hari dalam minggu mengandung karakter khusus XML, lakukan escape pada 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 menunjukkan hari dua digit. MMM menunjukkan singkatan tiga huruf atau nama lengkap bulan. YYYY menunjukkan tahun empat digit yang diawali 1 atau 2.
01 ~ 10 Jul, 2020
the first to the tenth of july twenty twenty
05&06 Apr, 2009
the fifth and the sixth of april two thousand nine
MMM DD-DD
atau
MMM DD~DD
atau
MMM DD&DD
Feb 01 - 03
feburary the first to the third
MMM menunjukkan singkatan tiga huruf atau nama lengkap bulan. DD menunjukkan hari dua 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 menunjukkan singkatan tiga huruf atau nama lengkap bulan.
Jul - Dec
july to dcember
sep&oct
september and october
YYYY-YYYY
atau
YYYY~YYYY
1990 - 2000
nineteen ninety to two thousand
YYYY menunjukkan tahun empat digit yang diawali 1 atau 2.
2001–2021
two thousand one to twenty twenty one
WWW DD MMM YYYY
Sun 20 Nov 2011
sunday the twentieth of november twenty eleven
WWW adalah singkatan tiga huruf atau nama lengkap hari dalam minggu. DD adalah hari dua digit. MMM adalah singkatan tiga huruf atau nama lengkap bulan. MM adalah bulan dua digit (atau singkatan tiga huruf atau nama lengkap bulan). YYYY adalah tahun empat digit yang 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
saturday 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
Format
Contoh
Output Bahasa Inggris
Deskripsi
HH:MM AM atau PM
09:00 AM
nine A M
HH mewakili jam satu atau dua digit. MM mewakili menit dua digit. AM/PM mewakili pagi atau 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 dan rentang waktu umum.
7:05~10:15 AM
seven oh five to ten fifteen A M
09:00-13:00
nine oclock to thirteen hundred
currency
Format
Contoh
Output Bahasa Inggris
Deskripsi
Angka + Identifikasi mata uang
1.00 RMB
one yuan
Format angka yang didukung: bilangan bulat, desimal, dan format internasional yang menggunakan koma sebagai pemisah ribuan.
Identifikasi mata uang yang didukung:
CN¥ (yuan)
CNY (yuan)
RMB (yuan)
AUD (australian dollar)
CAD (canadian dollar)
CHF (swiss franc)
DKK (danish krone)
EUR (euro)
GBP (british pound)
HKD (Hong Kong(China) dollar)
JPY (japanese yen)
NOK (norwegian krone)
SEK (swedish krona)
SGD (singapore dollar)
USD (united states dollar)
2.02 CNY
two point zero two yuan
1,000.23 CN¥
one thousand point two three yuan
1.01 SGD
one singapore dollar and one cent
2.01 CAD
two canadian dollars and one cent
3.1 HKD
three hong kong dollars and ten cents
1,000.00 EUR
one thousand euros
Identifikasi mata uang + Angka
US$ 1.00
one US dollar
Format angka yang didukung: bilangan bulat, desimal, dan format internasional yang menggunakan koma sebagai pemisah ribuan.
Identifikasi mata uang yang didukung:
US$ (US dollar)
CA$ (Canadian dollar)
AU$ (Australian dollar)
SG$ (Singapore dollar)
HK$ (Hong Kong(China) dollar)
C$ (Canadian dollar)
A$ (Australian dollar)
$ (dollar)
£ (pound)
€ (euro)
CN¥ (yuan)
CNY (yuan)
RMB (yuan)
AUD (australian dollar)
CAD (canadian dollar)
CHF (swiss franc)
DKK (danish krone)
EUR (euro)
GBP (british pound)
HKD (Hong Kong (China) dollar)
JPY (japanese yen)
NOK (norwegian krone)
SEK (swedish krona)
SGD (singapore dollar)
USD (united states dollar)
$0.01
one cent
JPY 1.01
one japanese yen and one sen
£1.1
one pound and ten pence
€2.01
two euros and one cent
USD 1,000
one thousand united states dollars
Angka + Kuantifier + Identifikasi mata uang
atau
Identifikasi mata uang + Angka + Kuantifier
1.23 Tn RMB
one point two three trillion yuan
Format kuantifier yang didukung meliputi:
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
Format
Contoh
Output Bahasa Inggris
Deskripsi
Angka + Satuan pengukuran
1.0 kg
one kilogram
Mendukung bilangan bulat, desimal, dan notasi internasional dengan pemisah koma.
Mendukung singkatan satuan umum.
1,234.01 km
one thousand two hundred thirty-four point zero one kilometers
Satuan pengukuran
mm2
square millimeter
Tabel berikut mencantumkan pelafalan simbol umum untuk <say-as>.
Simbol
Pelafalan Bahasa Inggris
!
exclamation mark
“
double quote
#
pound
$
dollar
%
percent
&
and
‘
left quote
(
left parenthesis
)
right parenthesis
*
asterisk
+
plus
,
comma
-
dash
.
dot
/
slash
:
Solon
;
semicolon
<
less than
=
equals
>
greater than
?
question mark
@
at
[
left bracket
\
backslash
]
right bracket
^
caret
_
underscore
`
backtick
{
left brace
|
vertical bar
}
right brace
~
tilde
!
exclamation mark
“
left double quote
”
right double quote
‘
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
Φ
phi
Χ
chi
Ψ
psi
Ω
omega
α
alpha
β
beta
γ
gamma
δ
delta
ε
epsilon
ζ
zeta
η
eta
θ
theta
ι
iota
κ
kappa
λ
lambda
μ
mu
ν
nu
ξ
ksi
ο
omicron
π
pi
ρ
rho
σ
sigma
τ
tau
υ
upsilon
φ
phi
χ
chi
ψ
psi
ω
omega
Tabel berikut mencantumkan satuan pengukuran umum untuk <say-as>.
Format
Kategori
Contoh Bahasa Inggris
Singkatan
Panjang
nm (nanometer), μm (micrometer), mm (millimeter), cm (centimeter), m (meter), km (kilometer), ft (foot), in (inch)
Luas
cm² (square centimeter), m² (square meter), km² (square kilometer), SqFt (square foot)
Volume
cm³ (cubic centimeter), m³ (cubic meter), km3 (cubic kilometer), mL (milliliter), L (liter), gal (gallon)
Berat
μg (microgram), mg (milligram), g (gram), kg (kilogram)
Waktu
min (minute), sec (second), ms (millisecond)
Elektromagnetisme
μA (microamp), mA (milliamp), Hz (hertz), kHz (kilohertz), MHz (megahertz), GHz (gigahertz), V (volt), kV (kilovolt), kWh (kilowatt hour)
Suara
dB (decibel)
Tekanan atmosfer
Pa (pascal), kPa (kilopascal), MPa (megapascal)
Satuan umum lainnya
Mendukung satuan pengukuran yang tidak terbatas pada kategori sebelumnya, seperti tsp (teaspoon), rpm (revolutions per minute), KB (kilobyte), dan mmHg (millimetre of mercury).
Hubungan
Tag <say-as> dapat berisi teks dan tag <vhml/>.
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> Her former name is <say-as interpret-as="name">Zeng Xiaofan</say-as> </speak>address
<speak> <say-as interpret-as="address">Fulu International, Building 1, Unit 3, Room 304</say-as> </speak>id
<speak> <say-as interpret-as="id">myid_1998</say-as> </speak>characters
<speak> <say-as interpret-as="characters">Greek letters αβ</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>