SSML(Speech Synthesis Markup Language) 是一種基於 XML 的語音合成標記語言。它不僅能讓語音合成大模型讀出更豐富的常值內容,還支援對語速、語調、停頓、音量等語音特徵進行精細控制,甚至可以添加背景音樂,帶來更具表現力的語音效果。本文介紹CosyVoice的SSML功能及使用。
如需使用“中國大陸(北京)”地區的模型,請前往“中國大陸(北京)”地區的API-KEY頁面
限制與約束
模型:僅cosyvoice-v3-flash、cosyvoice-v3-plus和cosyvoice-v2模型支援SSML功能
音色:僅複刻音色,以及音色列表中標記為支援SSML的系統音色支援SSML功能
介面:僅部分介面支援SSML功能
Java SDK(不低於2.20.3版本):僅非流式調用和單向流式調用支援SSML(詳情請參見:SSML標記語言支援說明-Java SDK)
Python SDK(不低於1.23.4版本):僅非流式調用和單向流式調用支援SSML(詳情請參見:SSML標記語言支援說明-Python SDK)
WebSocket API:在發送run-task指令時,必須將參數
enable_ssml設定為true,且只允許發送一次continue-task指令(詳情請參見:SSML標記語言支援說明-WebSocket API)。
快速開始
運行代碼前,請完成以下準備工作:
安裝SDK(如需運行Java/Python SDK樣本)
Java SDK
非流式調用
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;
/**
* SSML功能說明:
* 1. 只有非流式調用和單向流式調用支援SSML功能
* 2. 只有cosyvoice-v3-flash、cosyvoice-v3-plus和cosyvoice-v2模型的複刻音色以及音色列表中標記為支援SSML的系統音色支援SSML功能(例如cosyvoice-v3-flash模型的longanyang音色)
*/
public class Main {
private static String model = "cosyvoice-v3-flash";
private static String voice = "longanyang";
public static void main(String[] args) {
streamAudioDataToSpeaker();
System.exit(0);
}
public static void streamAudioDataToSpeaker() {
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// 若沒有配置環境變數,請用百鍊API Key將下行替換為:.apikey("sk-xxx")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model(model)
.voice(voice)
.build();
SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
ByteBuffer audio = null;
try {
// 非流式調用,阻塞直至音頻返回
// 特殊字元需要進行轉義
audio = synthesizer.call("<speak rate=\"2\">我的語速比正常人快。</speak>");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// 任務結束關閉websocket串連
synthesizer.getDuplexApi().close(1000, "bye");
}
if (audio != null) {
// 將音頻資料儲存到本地檔案“output.mp3”中
File file = new File("output.mp3");
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(audio.array());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// 首次發送文本時需建立 WebSocket 串連,因此首包延遲會包含串連建立的耗時
System.out.println(
"[Metric] requestId為:"
+ synthesizer.getLastRequestId()
+ "首包延遲(毫秒)為:"
+ synthesizer.getFirstPackageDelay());
}
}單向流式調用
import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisAudioFormat;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.common.ResultCallback;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
/**
* SSML功能說明:
* 1. 只有非流式調用和單向流式調用支援SSML功能
* 2. 只有cosyvoice-v3-flash、cosyvoice-v3-plus和cosyvoice-v2模型的複刻音色以及音色列表中標記為支援SSML的系統音色支援SSML功能(例如cosyvoice-v3-flash模型的longanyang音色)
*/
public class Main {
private static String model = "cosyvoice-v3-flash";
private static String voice = "longanyang";
public static void main(String[] args) {
streamAudioDataToSpeaker();
System.out.println("音頻已儲存到 output.mp3 檔案中");
System.exit(0);
}
public static void streamAudioDataToSpeaker() {
CountDownLatch latch = new CountDownLatch(1);
final FileOutputStream[] fileOutputStream = new FileOutputStream[1];
try {
fileOutputStream[0] = new FileOutputStream("output.mp3");
} catch (IOException e) {
System.err.println("無法建立輸出檔案: " + e.getMessage());
return;
}
// 實現回調介面ResultCallback
ResultCallback<SpeechSynthesisResult> callback = new ResultCallback<SpeechSynthesisResult>() {
@Override
public void onEvent(SpeechSynthesisResult result) {
if (result.getAudioFrame() != null) {
// 將音頻資料寫入本地檔案
try {
byte[] audioData = result.getAudioFrame().array();
fileOutputStream[0].write(audioData);
fileOutputStream[0].flush();
} catch (IOException e) {
System.err.println("寫入音頻資料失敗: " + e.getMessage());
}
}
}
@Override
public void onComplete() {
System.out.println("收到Complete,語音合成結束");
closeFileOutputStream(fileOutputStream[0]);
latch.countDown();
}
@Override
public void onError(Exception e) {
System.out.println("出現異常:" + e.toString());
closeFileOutputStream(fileOutputStream[0]);
latch.countDown();
}
};
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// 若沒有配置環境變數,請用百鍊API Key將下行替換為:.apikey("sk-xxx")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model(model)
.voice(voice)
.format(SpeechSynthesisAudioFormat.MP3_22050HZ_MONO_256KBPS)
.build();
SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, callback);
try {
// 單向流式調用,立即返回null(實際結果通過回調介面非同步傳遞),在回調介面的onEvent方法中即時擷取二進位音頻
// 特殊字元需要進行轉義
synthesizer.call("<speak rate=\"2\">我的語速比正常人快。</speak>");
// 等待合成完成
latch.await();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// 任務結束後關閉websocket串連
try {
synthesizer.getDuplexApi().close(1000, "bye");
} catch (Exception e) {
System.err.println("關閉WebSocket串連失敗: " + e.getMessage());
}
// 確保檔案流被關閉
closeFileOutputStream(fileOutputStream[0]);
}
// 首次發送文本時需建立 WebSocket 串連,因此首包延遲會包含串連建立的耗時
System.out.println(
"[Metric] requestId為:"
+ synthesizer.getLastRequestId()
+ ",首包延遲(毫秒)為:"
+ synthesizer.getFirstPackageDelay());
}
private static void closeFileOutputStream(FileOutputStream fileOutputStream) {
try {
if (fileOutputStream != null) {
fileOutputStream.close();
}
} catch (IOException e) {
System.err.println("關閉檔案流失敗: " + e.getMessage());
}
}
}Python SDK
非流式調用
# coding=utf-8
# SSML功能說明:
# 1. 只有非流式調用和單向流式調用支援SSML功能
# 2. 只有cosyvoice-v3-flash、cosyvoice-v3-plus和cosyvoice-v2模型的複刻音色以及音色列表中標記為支援SSML的系統音色支援SSML功能(例如cosyvoice-v3-flash模型的longanyang音色)
import dashscope
from dashscope.audio.tts_v2 import *
import os
# 若沒有配置環境變數,請用百鍊API Key將下行替換為:dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# 模型
model = "cosyvoice-v3-flash"
# 音色
voice = "longanyang"
# 執行個體化SpeechSynthesizer,並在構造方法中傳入模型(model)、音色(voice)等請求參數
synthesizer = SpeechSynthesizer(model=model, voice=voice)
# 非流式調用,阻塞直至音頻返回
# 特殊字元需要進行轉義
audio = synthesizer.call("<speak rate=\"2\">我的語速比正常人快。</speak>")
# 將音頻儲存至本地
with open('output.mp3', 'wb') as f:
f.write(audio)
# 首次發送文本時需建立 WebSocket 串連,因此首包延遲會包含串連建立的耗時
print('[Metric] requestId為:{},首包延遲為:{}毫秒'.format(
synthesizer.get_last_request_id(),
synthesizer.get_first_package_delay()))單向流式調用
# coding=utf-8
# SSML功能說明:
# 1. 只有非流式調用和單向流式調用支援SSML功能
# 2. 只有cosyvoice-v3-flash、cosyvoice-v3-plus和cosyvoice-v2模型的複刻音色以及音色列表中標記為支援SSML的系統音色支援SSML功能(例如cosyvoice-v3-flash模型的longanyang音色)
import dashscope
from dashscope.audio.tts_v2 import *
import os
from datetime import datetime
def get_timestamp():
now = datetime.now()
formatted_timestamp = now.strftime("[%Y-%m-%d %H:%M:%S.%f]")
return formatted_timestamp
# 若沒有配置環境變數,請用百鍊API Key將下行替換為:dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# 模型
model = "cosyvoice-v3-flash"
# 音色
voice = "longanyang"
# 定義回調介面
class Callback(ResultCallback):
_player = None
_stream = None
def on_open(self):
# 開啟輸出檔案,準備寫入音頻資料
self.file = open("output.mp3", "wb")
print("串連建立:" + get_timestamp())
def on_complete(self):
print("語音合成完成,所有合成結果已被接收:" + get_timestamp())
if hasattr(self, 'file') and self.file:
self.file.close()
self
# 首次發送文本時需建立 WebSocket 串連,因此首包延遲會包含串連建立的耗時
print('[Metric] requestId為:{},首包延遲為:{}毫秒'.format(
self.synthesizer.get_last_request_id(),
self.synthesizer.get_first_package_delay()))
def on_error(self, message: str):
print(f"語音合成出現異常:{message}")
if hasattr(self, 'file') and self.file:
self.file.close()
def on_close(self):
print("串連關閉:" + get_timestamp())
if hasattr(self, 'file') and self.file:
self.file.close()
def on_event(self, message):
pass
def on_data(self, data: bytes) -> None:
print(get_timestamp() + " 二進位音頻長度為:" + str(len(data)))
# 將音頻資料寫入檔案
self.file.write(data)
callback = Callback()
# 執行個體化SpeechSynthesizer,並在構造方法中傳入模型(model)、音色(voice)等請求參數
synthesizer = SpeechSynthesizer(
model=model,
voice=voice,
callback=callback,
)
# 將synthesizer執行個體賦值給callback,以便在on_complete中使用
callback.synthesizer = synthesizer
# 單向流式調用,發送待合成文本,在回調介面的on_data方法中即時擷取二進位音頻
# 特殊字元需要進行轉義
synthesizer.call("<speak rate=\"2\">我的語速比正常人快。</speak>")WebSocket API
Go
// SSML功能說明:
// 1. 在發送run-task指令時,將參數enable_ssml設定為true,以開啟SSML支援
// 2. 通過continue-task指令發送包含SSML的文本,且只允許發送一次continue-task指令
// 3. 只有cosyvoice-v3-flash、cosyvoice-v3-plus和cosyvoice-v2模型的複刻音色以及音色列表中標記為支援SSML的系統音色支援SSML功能(例如cosyvoice-v3-flash模型的longanyang音色)
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
"time"
"github.com/google/uuid"
"github.com/gorilla/websocket"
)
const (
wsURL = "wss://dashscope.aliyuncs.com/api-ws/v1/inference/"
outputFile = "output.mp3"
)
func main() {
// 若沒有將API Key配置到環境變數,可將下行替換為:apiKey := "your_api_key"。不建議在生產環境中直接將API Key寫入程式碼到代碼中,以減少API Key泄露風險。
apiKey := os.Getenv("DASHSCOPE_API_KEY")
// 清空輸出檔案
os.Remove(outputFile)
os.Create(outputFile)
// 串連WebSocket
header := make(http.Header)
header.Add("X-DashScope-DataInspection", "enable")
header.Add("Authorization", fmt.Sprintf("bearer %s", apiKey))
conn, resp, err := websocket.DefaultDialer.Dial(wsURL, header)
if err != nil {
if resp != nil {
fmt.Printf("串連失敗 HTTP狀態代碼: %d\n", resp.StatusCode)
}
fmt.Println("串連失敗:", err)
return
}
defer conn.Close()
// 產生任務ID
taskID := uuid.New().String()
fmt.Printf("產生任務ID: %s\n", taskID)
// 發送run-task指令
runTaskCmd := map[string]interface{}{
"header": map[string]interface{}{
"action": "run-task",
"task_id": taskID,
"streaming": "duplex",
},
"payload": map[string]interface{}{
"task_group": "audio",
"task": "tts",
"function": "SpeechSynthesizer",
"model": "cosyvoice-v3-flash",
"parameters": map[string]interface{}{
"text_type": "PlainText",
"voice": "longanyang",
"format": "mp3",
"sample_rate": 22050,
"volume": 50,
"rate": 1,
"pitch": 1,
// 如果enable_ssml設為true,只允許發送一次continue-task指令,否則會報錯“Text request limit violated, expected 1.”
"enable_ssml": true,
},
"input": map[string]interface{}{},
},
}
runTaskJSON, _ := json.Marshal(runTaskCmd)
fmt.Printf("發送run-task指令: %s\n", string(runTaskJSON))
err = conn.WriteMessage(websocket.TextMessage, runTaskJSON)
if err != nil {
fmt.Println("發送run-task失敗:", err)
return
}
textSent := false
// 處理訊息
for {
messageType, message, err := conn.ReadMessage()
if err != nil {
fmt.Println("讀取訊息失敗:", err)
break
}
// 處理二進位訊息
if messageType == websocket.BinaryMessage {
fmt.Printf("收到二進位訊息,長度: %d\n", len(message))
file, _ := os.OpenFile(outputFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
file.Write(message)
file.Close()
continue
}
// 處理簡訊
messageStr := string(message)
fmt.Printf("收到簡訊: %s\n", strings.ReplaceAll(messageStr, "\n", ""))
// 簡單解析JSON擷取event類型
var msgMap map[string]interface{}
if json.Unmarshal(message, &msgMap) == nil {
if header, ok := msgMap["header"].(map[string]interface{}); ok {
if event, ok := header["event"].(string); ok {
fmt.Printf("事件類型: %s\n", event)
switch event {
case "task-started":
fmt.Println("=== 收到task-started事件 ===")
if !textSent {
// 發送 continue-task 指令,使用SSML功能時,該指令只允許發送一次
continueTaskCmd := map[string]interface{}{
"header": map[string]interface{}{
"action": "continue-task",
"task_id": taskID,
"streaming": "duplex",
},
"payload": map[string]interface{}{
"input": map[string]interface{}{
// 特殊字元需要進行轉義
"text": "<speak rate=\"2\">我的語速比正常人快。</speak>",
},
},
}
continueTaskJSON, _ := json.Marshal(continueTaskCmd)
fmt.Printf("發送continue-task指令: %s\n", string(continueTaskJSON))
err = conn.WriteMessage(websocket.TextMessage, continueTaskJSON)
if err != nil {
fmt.Println("發送continue-task失敗:", err)
return
}
textSent = true
// 延遲發送finish-task
time.Sleep(500 * time.Millisecond)
// 發送finish-task指令
finishTaskCmd := map[string]interface{}{
"header": map[string]interface{}{
"action": "finish-task",
"task_id": taskID,
"streaming": "duplex",
},
"payload": map[string]interface{}{
"input": map[string]interface{}{},
},
}
finishTaskJSON, _ := json.Marshal(finishTaskCmd)
fmt.Printf("發送finish-task指令: %s\n", string(finishTaskJSON))
err = conn.WriteMessage(websocket.TextMessage, finishTaskJSON)
if err != nil {
fmt.Println("發送finish-task失敗:", err)
return
}
}
case "task-finished":
fmt.Println("=== 任務完成 ===")
return
case "task-failed":
fmt.Println("=== 任務失敗 ===")
if header["error_message"] != nil {
fmt.Printf("錯誤資訊: %s\n", header["error_message"])
}
return
case "result-generated":
fmt.Println("收到result-generated事件")
}
}
}
}
}
}
C#
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
// SSML功能說明:
// 1. 在發送run-task指令時,將參數enable_ssml設定為true,以開啟SSML支援
// 2. 通過continue-task指令發送包含SSML的文本,且只允許發送一次continue-task指令
// 3. 只有cosyvoice-v3-flash、cosyvoice-v3-plus和cosyvoice-v2模型的複刻音色以及音色列表中標記為支援SSML的系統音色支援SSML功能(例如cosyvoice-v3-flash模型的longanyang音色)
class Program {
// 若沒有將API Key配置到環境變數,可將下行替換為:private const string ApiKey="your_api_key"。不建議在生產環境中直接將API Key寫入程式碼到代碼中,以減少API Key泄露風險。
private static readonly string ApiKey = Environment.GetEnvironmentVariable("DASHSCOPE_API_KEY") ?? throw new InvalidOperationException("DASHSCOPE_API_KEY environment variable is not set.");
// WebSocket伺服器位址
private const string WebSocketUrl = "wss://dashscope.aliyuncs.com/api-ws/v1/inference/";
// 輸出檔案路徑
private const string OutputFilePath = "output.mp3";
// WebSocket用戶端
private static ClientWebSocket _webSocket = new ClientWebSocket();
// 取消令牌源
private static CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
// 任務ID
private static string? _taskId;
// 任務是否已啟動
private static TaskCompletionSource<bool> _taskStartedTcs = new TaskCompletionSource<bool>();
static async Task Main(string[] args) {
try {
// 清空輸出檔案
ClearOutputFile(OutputFilePath);
// 串連WebSocket服務
await ConnectToWebSocketAsync(WebSocketUrl);
// 啟動接收訊息的任務
Task receiveTask = ReceiveMessagesAsync();
// 發送run-task指令
_taskId = GenerateTaskId();
await SendRunTaskCommandAsync(_taskId);
// 等待task-started事件
await _taskStartedTcs.Task;
// 發送 continue-task 指令,使用SSML功能時,該指令只允許發送一次
// 特殊字元需要進行轉義
await SendContinueTaskCommandAsync("<speak rate=\"2\">我的語速比正常人快。</speak>");
// 發送finish-task指令
await SendFinishTaskCommandAsync(_taskId);
// 等待接收任務完成
await receiveTask;
Console.WriteLine("任務完成,串連已關閉。");
} catch (OperationCanceledException) {
Console.WriteLine("任務被取消。");
} catch (Exception ex) {
Console.WriteLine($"發生錯誤:{ex.Message}");
} finally {
_cancellationTokenSource.Cancel();
_webSocket.Dispose();
}
}
private static void ClearOutputFile(string filePath) {
if (File.Exists(filePath)) {
File.WriteAllText(filePath, string.Empty);
Console.WriteLine("輸出檔案已清空。");
} else {
Console.WriteLine("輸出檔案不存在,無需清空。");
}
}
private static async Task ConnectToWebSocketAsync(string url) {
var uri = new Uri(url);
if (_webSocket.State == WebSocketState.Connecting || _webSocket.State == WebSocketState.Open) {
return;
}
// 設定WebSocket串連的頭部資訊
_webSocket.Options.SetRequestHeader("Authorization", $"bearer {ApiKey}");
_webSocket.Options.SetRequestHeader("X-DashScope-DataInspection", "enable");
try {
await _webSocket.ConnectAsync(uri, _cancellationTokenSource.Token);
Console.WriteLine("已成功串連到WebSocket服務。");
} catch (OperationCanceledException) {
Console.WriteLine("WebSocket串連被取消。");
} catch (Exception ex) {
Console.WriteLine($"WebSocket串連失敗: {ex.Message}");
throw;
}
}
private static async Task SendRunTaskCommandAsync(string taskId) {
var command = CreateCommand("run-task", taskId, "duplex", new {
task_group = "audio",
task = "tts",
function = "SpeechSynthesizer",
model = "cosyvoice-v3-flash",
parameters = new
{
text_type = "PlainText",
voice = "longanyang",
format = "mp3",
sample_rate = 22050,
volume = 50,
rate = 1,
pitch = 1,
// 如果enable_ssml設為true,只允許發送一次continue-task指令,否則會報錯“Text request limit violated, expected 1.”
enable_ssml = true
},
input = new { }
});
await SendJsonMessageAsync(command);
Console.WriteLine("已發送run-task指令。");
}
private static async Task SendContinueTaskCommandAsync(string text) {
if (_taskId == null) {
throw new InvalidOperationException("任務ID未初始化。");
}
var command = CreateCommand("continue-task", _taskId, "duplex", new {
input = new {
text
}
});
await SendJsonMessageAsync(command);
Console.WriteLine("已發送continue-task指令。");
}
private static async Task SendFinishTaskCommandAsync(string taskId) {
var command = CreateCommand("finish-task", taskId, "duplex", new {
input = new { }
});
await SendJsonMessageAsync(command);
Console.WriteLine("已發送finish-task指令。");
}
private static async Task SendJsonMessageAsync(string message) {
var buffer = Encoding.UTF8.GetBytes(message);
try {
await _webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, _cancellationTokenSource.Token);
} catch (OperationCanceledException) {
Console.WriteLine("訊息發送被取消。");
}
}
private static async Task ReceiveMessagesAsync() {
while (_webSocket.State == WebSocketState.Open) {
var response = await ReceiveMessageAsync();
if (response != null) {
var eventStr = response.RootElement.GetProperty("header").GetProperty("event").GetString();
switch (eventStr) {
case "task-started":
Console.WriteLine("任務已啟動。");
_taskStartedTcs.TrySetResult(true);
break;
case "task-finished":
Console.WriteLine("任務已完成。");
_cancellationTokenSource.Cancel();
break;
case "task-failed":
Console.WriteLine("任務失敗:" + response.RootElement.GetProperty("header").GetProperty("error_message").GetString());
_cancellationTokenSource.Cancel();
break;
default:
// result-generated可在此處理
break;
}
}
}
}
private static async Task<JsonDocument?> ReceiveMessageAsync() {
var buffer = new byte[1024 * 4];
var segment = new ArraySegment<byte>(buffer);
try {
WebSocketReceiveResult result = await _webSocket.ReceiveAsync(segment, _cancellationTokenSource.Token);
if (result.MessageType == WebSocketMessageType.Close) {
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", _cancellationTokenSource.Token);
return null;
}
if (result.MessageType == WebSocketMessageType.Binary) {
// 處理位元據
Console.WriteLine("接收到位元據...");
// 將位元據儲存到檔案
using (var fileStream = new FileStream(OutputFilePath, FileMode.Append)) {
fileStream.Write(buffer, 0, result.Count);
}
return null;
}
string message = Encoding.UTF8.GetString(buffer, 0, result.Count);
return JsonDocument.Parse(message);
} catch (OperationCanceledException) {
Console.WriteLine("訊息接收被取消。");
return null;
}
}
private static string GenerateTaskId() {
return Guid.NewGuid().ToString("N").Substring(0, 32);
}
private static string CreateCommand(string action, string taskId, string streaming, object payload) {
var command = new {
header = new {
action,
task_id = taskId,
streaming
},
payload
};
return JsonSerializer.Serialize(command);
}
}PHP
範例程式碼目錄結構為:
my-php-project/
├── composer.json
├── vendor/
└── index.php
composer.json內容如下,相關依賴的版本號碼請根據實際情況自行決定:
{
"require": {
"react/event-loop": "^1.3",
"react/socket": "^1.11",
"react/stream": "^1.2",
"react/http": "^1.1",
"ratchet/pawl": "^0.4"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}index.php內容如下:
<!-- SSML功能說明: -->
<!-- 1. 在發送run-task指令時,將參數enable_ssml設定為true,以開啟SSML支援 -->
<!-- 2. 通過continue-task指令發送包含SSML的文本,且只允許發送一次continue-task指令 -->
<!-- 3. 只有cosyvoice-v3-flash、cosyvoice-v3-plus和cosyvoice-v2模型的複刻音色以及音色列表中標記為支援SSML的系統音色支援SSML功能(例如cosyvoice-v3-flash模型的longanyang音色) -->
<?php
require __DIR__ . '/vendor/autoload.php';
use Ratchet\Client\Connector;
use React\EventLoop\Loop;
use React\Socket\Connector as SocketConnector;
// 若沒有將API Key配置到環境變數,可將下行替換為:$api_key="your_api_key"。不建議在生產環境中直接將API Key寫入程式碼到代碼中,以減少API Key泄露風險。
$api_key = getenv("DASHSCOPE_API_KEY");
$websocket_url = 'wss://dashscope.aliyuncs.com/api-ws/v1/inference/'; // WebSocket伺服器位址
$output_file = 'output.mp3'; // 輸出檔案路徑
$loop = Loop::get();
if (file_exists($output_file)) {
// 清空檔案內容
file_put_contents($output_file, '');
}
// 建立自訂的連接器
$socketConnector = new SocketConnector($loop, [
'tcp' => [
'bindto' => '0.0.0.0:0',
],
'tls' => [
'verify_peer' => false,
'verify_peer_name' => false,
],
]);
$connector = new Connector($loop, $socketConnector);
$headers = [
'Authorization' => 'bearer ' . $api_key,
'X-DashScope-DataInspection' => 'enable'
];
$connector($websocket_url, [], $headers)->then(function ($conn) use ($loop, $output_file) {
echo "串連到WebSocket伺服器\n";
// 產生任務ID
$taskId = generateTaskId();
// 發送 run-task 指令
sendRunTaskMessage($conn, $taskId);
// 定義發送 continue-task 指令的函數
$sendContinueTask = function() use ($conn, $loop, $taskId) {
// 發送 continue-task 指令,使用SSML功能時,該指令只允許發送一次
$continueTaskMessage = json_encode([
"header" => [
"action" => "continue-task",
"task_id" => $taskId,
"streaming" => "duplex"
],
"payload" => [
"input" => [
// 特殊字元需要進行轉義
"text" => "<speak rate=\"2\">我的語速比正常人快。</speak>"
]
]
]);
$conn->send($continueTaskMessage);
// 發送 finish-task 指令
sendFinishTaskMessage($conn, $taskId);
};
// 標記是否收到 task-started 事件
$taskStarted = false;
// 監聽訊息
$conn->on('message', function($msg) use ($conn, $sendContinueTask, $loop, &$taskStarted, $taskId, $output_file) {
if ($msg->isBinary()) {
// 寫入位元據到本地檔案
file_put_contents($output_file, $msg->getPayload(), FILE_APPEND);
} else {
// 處理非二進位訊息
$response = json_decode($msg, true);
if (isset($response['header']['event'])) {
handleEvent($conn, $response, $sendContinueTask, $loop, $taskId, $taskStarted);
} else {
echo "未知的訊息格式\n";
}
}
});
// 監聽串連關閉
$conn->on('close', function($code = null, $reason = null) {
echo "串連已關閉\n";
if ($code !== null) {
echo "關閉代碼: " . $code . "\n";
}
if ($reason !== null) {
echo "關閉原因:" . $reason . "\n";
}
});
}, function ($e) {
echo "無法串連:{$e->getMessage()}\n";
});
$loop->run();
/**
* 產生任務ID
* @return string
*/
function generateTaskId(): string {
return bin2hex(random_bytes(16));
}
/**
* 發送 run-task 指令
* @param $conn
* @param $taskId
*/
function sendRunTaskMessage($conn, $taskId) {
$runTaskMessage = json_encode([
"header" => [
"action" => "run-task",
"task_id" => $taskId,
"streaming" => "duplex"
],
"payload" => [
"task_group" => "audio",
"task" => "tts",
"function" => "SpeechSynthesizer",
"model" => "cosyvoice-v3-flash",
"parameters" => [
"text_type" => "PlainText",
"voice" => "longanyang",
"format" => "mp3",
"sample_rate" => 22050,
"volume" => 50,
"rate" => 1,
"pitch" => 1,
// 如果enable_ssml設為true,只允許發送一次continue-task指令,否則會報錯“Text request limit violated, expected 1.”
"enable_ssml" => true
],
"input" => (object) []
]
]);
echo "準備發送run-task指令: " . $runTaskMessage . "\n";
$conn->send($runTaskMessage);
echo "run-task指令已發送\n";
}
/**
* 讀取音頻檔案
* @param string $filePath
* @return bool|string
*/
function readAudioFile(string $filePath) {
$voiceData = file_get_contents($filePath);
if ($voiceData === false) {
echo "無法讀取音頻檔案\n";
}
return $voiceData;
}
/**
* 分割音頻資料
* @param string $data
* @param int $chunkSize
* @return array
*/
function splitAudioData(string $data, int $chunkSize): array {
return str_split($data, $chunkSize);
}
/**
* 發送 finish-task 指令
* @param $conn
* @param $taskId
*/
function sendFinishTaskMessage($conn, $taskId) {
$finishTaskMessage = json_encode([
"header" => [
"action" => "finish-task",
"task_id" => $taskId,
"streaming" => "duplex"
],
"payload" => [
"input" => (object) []
]
]);
echo "準備發送finish-task指令: " . $finishTaskMessage . "\n";
$conn->send($finishTaskMessage);
echo "finish-task指令已發送\n";
}
/**
* 處理事件
* @param $conn
* @param $response
* @param $sendContinueTask
* @param $loop
* @param $taskId
* @param $taskStarted
*/
function handleEvent($conn, $response, $sendContinueTask, $loop, $taskId, &$taskStarted) {
switch ($response['header']['event']) {
case 'task-started':
echo "任務開始,發送continue-task指令...\n";
$taskStarted = true;
// 發送 continue-task 指令
$sendContinueTask();
break;
case 'result-generated':
// 忽略result-generated事件
break;
case 'task-finished':
echo "任務完成\n";
$conn->close();
break;
case 'task-failed':
echo "任務失敗\n";
echo "錯誤碼:" . $response['header']['error_code'] . "\n";
echo "錯誤資訊:" . $response['header']['error_message'] . "\n";
$conn->close();
break;
case 'error':
echo "錯誤:" . $response['payload']['message'] . "\n";
break;
default:
echo "未知事件:" . $response['header']['event'] . "\n";
break;
}
// 如果任務已完成,關閉串連
if ($response['header']['event'] == 'task-finished') {
// 等待1秒以確保所有資料都已傳輸完畢
$loop->addTimer(1, function() use ($conn) {
$conn->close();
echo "用戶端關閉串連\n";
});
}
// 如果沒有收到 task-started 事件,關閉串連
if (!$taskStarted && in_array($response['header']['event'], ['task-failed', 'error'])) {
$conn->close();
}
}Node.js
需安裝相關依賴:
npm install ws
npm install uuid範例程式碼如下:
// SSML功能說明:
// 1. 在發送run-task指令時,將參數enable_ssml設定為true,以開啟SSML支援
// 2. 通過continue-task指令發送包含SSML的文本,且只允許發送一次continue-task指令
// 3. 只有cosyvoice-v3-flash、cosyvoice-v3-plus和cosyvoice-v2模型的複刻音色以及音色列表中標記為支援SSML的系統音色支援SSML功能(例如cosyvoice-v3-flash模型的longanyang音色)
import fs from 'fs';
import WebSocket from 'ws';
import { v4 as uuid } from 'uuid'; // 用於產生UUID
// 若沒有將API Key配置到環境變數,可將下行替換為:apiKey = 'your_api_key'。不建議在生產環境中直接將API Key寫入程式碼到代碼中,以減少API Key泄露風險。
const apiKey = process.env.DASHSCOPE_API_KEY;
// WebSocket伺服器位址
const url = 'wss://dashscope.aliyuncs.com/api-ws/v1/inference/';
// 輸出檔案路徑
const outputFilePath = 'output.mp3';
// 清空輸出檔案
fs.writeFileSync(outputFilePath, '');
// 建立WebSocket用戶端
const ws = new WebSocket(url, {
headers: {
Authorization: `bearer ${apiKey}`,
'X-DashScope-DataInspection': 'enable'
}
});
let taskStarted = false;
let taskId = uuid();
ws.on('open', () => {
console.log('已串連到WebSocket伺服器');
// 發送run-task指令
const runTaskMessage = JSON.stringify({
header: {
action: 'run-task',
task_id: taskId,
streaming: 'duplex'
},
payload: {
task_group: 'audio',
task: 'tts',
function: 'SpeechSynthesizer',
model: 'cosyvoice-v3-flash',
parameters: {
text_type: 'PlainText',
voice: 'longanyang', // 音色
format: 'mp3', // 音頻格式
sample_rate: 22050, // 採樣率
volume: 50, // 音量
rate: 1, // 語速
pitch: 1, // 音調
enable_ssml: true // 是否開啟SSML功能。如果enable_ssml設為true,只允許發送一次continue-task指令,否則會報錯“Text request limit violated, expected 1.”
},
input: {}
}
});
ws.send(runTaskMessage);
console.log('已發送run-task訊息');
});
const fileStream = fs.createWriteStream(outputFilePath, { flags: 'a' });
ws.on('message', (data, isBinary) => {
if (isBinary) {
// 寫入位元據到檔案
fileStream.write(data);
} else {
const message = JSON.parse(data);
switch (message.header.event) {
case 'task-started':
taskStarted = true;
console.log('任務已開始');
// 發送continue-task指令
sendContinueTasks(ws);
break;
case 'task-finished':
console.log('任務已完成');
ws.close();
fileStream.end(() => {
console.log('檔案流已關閉');
});
break;
case 'task-failed':
console.error('任務失敗:', message.header.error_message);
ws.close();
fileStream.end(() => {
console.log('檔案流已關閉');
});
break;
default:
// 可以在這裡處理result-generated
break;
}
}
});
function sendContinueTasks(ws) {
if (taskStarted) {
// 發送 continue-task 指令,使用SSML功能時,該指令只允許發送一次
const continueTaskMessage = JSON.stringify({
header: {
action: 'continue-task',
task_id: taskId,
streaming: 'duplex'
},
payload: {
input: {
// 特殊字元需要進行轉義
text: '<speak rate="2">我的語速比正常人快。</speak>'
}
}
});
ws.send(continueTaskMessage);
// 發送finish-task指令
const finishTaskMessage = JSON.stringify({
header: {
action: 'finish-task',
task_id: taskId,
streaming: 'duplex'
},
payload: {
input: {}
}
});
ws.send(finishTaskMessage);
}
}
ws.on('close', () => {
console.log('已斷開與WebSocket伺服器的串連');
});Java
如您使用Java程式設計語言,建議採用Java DashScope SDK進行開發,詳情請參見Java SDK。
以下是Java WebSocket的調用樣本。在運行樣本前,請確保已匯入以下依賴:
Java-WebSocketjackson-databind
推薦您使用Maven或Gradle管理依賴包,其配置如下:
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
// 省略其它代碼
dependencies {
// WebSocket Client
implementation 'org.java-websocket:Java-WebSocket:1.5.3'
// JSON Processing
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0'
}
// 省略其它代碼Java代碼如下:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.*;
/**
* SSML功能說明:
* 1. 在發送run-task指令時,將參數enable_ssml設定為true,以開啟SSML支援
* 2. 通過continue-task指令發送包含SSML的文本,且只允許發送一次continue-task指令
* 3. 只有cosyvoice-v3-flash、cosyvoice-v3-plus和cosyvoice-v2模型的複刻音色以及音色列表中標記為支援SSML的系統音色支援SSML功能(例如cosyvoice-v3-flash模型的longanyang音色)
*/
public class TTSWebSocketClient extends WebSocketClient {
private final String taskId = UUID.randomUUID().toString();
private final String outputFile = "output_" + System.currentTimeMillis() + ".mp3";
private boolean taskFinished = false;
public TTSWebSocketClient(URI serverUri, Map<String, String> headers) {
super(serverUri, headers);
}
@Override
public void onOpen(ServerHandshake serverHandshake) {
System.out.println("串連成功");
// 發送run-task指令
// 如果enable_ssml設為true,只允許發送一次continue-task指令,否則會報錯“Text request limit violated, expected 1.”
String runTaskCommand = "{ \"header\": { \"action\": \"run-task\", \"task_id\": \"" + taskId + "\", \"streaming\": \"duplex\" }, \"payload\": { \"task_group\": \"audio\", \"task\": \"tts\", \"function\": \"SpeechSynthesizer\", \"model\": \"cosyvoice-v3-flash\", \"parameters\": { \"text_type\": \"PlainText\", \"voice\": \"longanyang\", \"format\": \"mp3\", \"sample_rate\": 22050, \"volume\": 50, \"rate\": 1, \"pitch\": 1, \"enable_ssml\": true }, \"input\": {} }}";
send(runTaskCommand);
}
@Override
public void onMessage(String message) {
System.out.println("收到服務端返回的訊息:" + message);
try {
// Parse JSON message
Map<String, Object> messageMap = new ObjectMapper().readValue(message, Map.class);
if (messageMap.containsKey("header")) {
Map<String, Object> header = (Map<String, Object>) messageMap.get("header");
if (header.containsKey("event")) {
String event = (String) header.get("event");
if ("task-started".equals(event)) {
System.out.println("收到服務端返回的task-started事件");
// 發送 continue-task 指令,使用SSML功能時,該指令只允許發送一次
// 特殊字元需要進行轉義
sendContinueTask("<speak rate=\\\"2\\\">我的語速比正常人快。</speak>");
// 發送finish-task指令
sendFinishTask();
} else if ("task-finished".equals(event)) {
System.out.println("收到服務端返回的task-finished事件");
taskFinished = true;
closeConnection();
} else if ("task-failed".equals(event)) {
System.out.println("任務失敗:" + message);
closeConnection();
}
}
}
} catch (Exception e) {
System.err.println("出現異常:" + e.getMessage());
}
}
@Override
public void onMessage(ByteBuffer message) {
System.out.println("收到的二進位音頻資料大小為:" + message.remaining());
try (FileOutputStream fos = new FileOutputStream(outputFile, true)) {
byte[] buffer = new byte[message.remaining()];
message.get(buffer);
fos.write(buffer);
System.out.println("音頻資料已寫入本地檔案" + outputFile + "中");
} catch (IOException e) {
System.err.println("音頻資料寫入本地檔案失敗:" + e.getMessage());
}
}
@Override
public void onClose(int code, String reason, boolean remote) {
System.out.println("串連關閉:" + reason + " (" + code + ")");
}
@Override
public void onError(Exception ex) {
System.err.println("報錯:" + ex.getMessage());
ex.printStackTrace();
}
private void sendContinueTask(String text) {
String command = "{ \"header\": { \"action\": \"continue-task\", \"task_id\": \"" + taskId + "\", \"streaming\": \"duplex\" }, \"payload\": { \"input\": { \"text\": \"" + text + "\" } }}";
send(command);
}
private void sendFinishTask() {
String command = "{ \"header\": { \"action\": \"finish-task\", \"task_id\": \"" + taskId + "\", \"streaming\": \"duplex\" }, \"payload\": { \"input\": {} }}";
send(command);
}
private void closeConnection() {
if (!isClosed()) {
close();
}
}
public static void main(String[] args) {
try {
String apiKey = System.getenv("DASHSCOPE_API_KEY");
if (apiKey == null || apiKey.isEmpty()) {
System.err.println("請設定 DASHSCOPE_API_KEY 環境變數");
return;
}
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "bearer " + apiKey);
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("串連WebSocket服務失敗:" + e.getMessage());
e.printStackTrace();
}
}
}Python
如您使用Python程式設計語言,建議採用Python DashScope SDK進行開發,詳情請參見Python SDK。
以下是Python WebSocket的調用樣本。在運行樣本前,請確保通過如下方式匯入依賴:
pip uninstall websocket-client
pip uninstall websocket
pip install websocket-client請不要將運行範例程式碼的Python檔案命名為“websocket.py”,否則會報錯(AttributeError: module 'websocket' has no attribute 'WebSocketApp'. Did you mean: 'WebSocket'?)。
# SSML功能說明:
# 1. 在發送run-task指令時,將參數enable_ssml設定為true,以開啟SSML支援
# 2. 通過continue-task指令發送包含SSML的文本,且只允許發送一次continue-task指令
# 3. 只有cosyvoice-v3-flash、cosyvoice-v3-plus和cosyvoice-v2模型的複刻音色以及音色列表中標記為支援SSML的系統音色支援SSML功能(例如cosyvoice-v3-flash模型的longanyang音色)
import websocket
import json
import uuid
import os
import time
class TTSClient:
def __init__(self, api_key, uri):
"""
初始化 TTSClient 執行個體
參數:
api_key (str): 鑒權用的 API Key
uri (str): WebSocket 服務地址
"""
self.api_key = api_key # 替換為你的 API Key
self.uri = uri # 替換為你的 WebSocket 地址
self.task_id = str(uuid.uuid4()) # 產生唯一任務 ID
self.output_file = f"output_{int(time.time())}.mp3" # 輸出音頻檔案路徑
self.ws = None # WebSocketApp 執行個體
self.task_started = False # 是否收到 task-started
self.task_finished = False # 是否收到 task-finished / task-failed
def on_open(self, ws):
"""
WebSocket 串連建立時回呼函數
發送 run-task 指令開啟語音合成任務
"""
print("WebSocket 已串連")
# 構造 run-task 指令
run_task_cmd = {
"header": {
"action": "run-task",
"task_id": self.task_id,
"streaming": "duplex"
},
"payload": {
"task_group": "audio",
"task": "tts",
"function": "SpeechSynthesizer",
"model": "cosyvoice-v3-flash",
"parameters": {
"text_type": "PlainText",
"voice": "longanyang",
"format": "mp3",
"sample_rate": 22050,
"volume": 50,
"rate": 1,
"pitch": 1,
# 如果enable_ssml設為True,只允許發送一次continue-task指令,否則會報錯“Text request limit violated, expected 1.”
"enable_ssml": True
},
"input": {}
}
}
# 發送 run-task 指令
ws.send(json.dumps(run_task_cmd))
print("已發送 run-task 指令")
def on_message(self, ws, message):
"""
接收到訊息時的回呼函數
區分文本和二進位訊息處理
"""
if isinstance(message, str):
# 處理 JSON 簡訊
try:
msg_json = json.loads(message)
print(f"收到 JSON 訊息: {msg_json}")
if "header" in msg_json:
header = msg_json["header"]
if "event" in header:
event = header["event"]
if event == "task-started":
print("任務已啟動")
self.task_started = True
# 發送 continue-task 指令,使用SSML功能時,該指令只允許發送一次
# 特殊字元需要進行轉義
self.send_continue_task("<speak rate=\"2\">我的語速比正常人快。</speak>")
# continue-task 發送完成後發送 finish-task
self.send_finish_task()
elif event == "task-finished":
print("任務已完成")
self.task_finished = True
self.close(ws)
elif event == "task-failed":
error_msg = msg_json.get("error_message", "未知錯誤")
print(f"任務失敗: {error_msg}")
self.task_finished = True
self.close(ws)
except json.JSONDecodeError as e:
print(f"JSON 解析失敗: {e}")
else:
# 處理二進位訊息(音頻資料)
print(f"收到二進位訊息,大小: {len(message)} 位元組")
with open(self.output_file, "ab") as f:
f.write(message)
print(f"已將音頻資料寫入本地檔案{self.output_file}中")
def on_error(self, ws, error):
"""發生錯誤時的回調"""
print(f"WebSocket 出錯: {error}")
def on_close(self, ws, close_status_code, close_msg):
"""串連關閉時的回調"""
print(f"WebSocket 已關閉: {close_msg} ({close_status_code})")
def send_continue_task(self, text):
"""發送 continue-task 指令,附帶要合成的常值內容"""
cmd = {
"header": {
"action": "continue-task",
"task_id": self.task_id,
"streaming": "duplex"
},
"payload": {
"input": {
"text": text
}
}
}
self.ws.send(json.dumps(cmd))
print(f"已發送 continue-task 指令,常值內容: {text}")
def send_finish_task(self):
"""發送 finish-task 指令,結束語音合成任務"""
cmd = {
"header": {
"action": "finish-task",
"task_id": self.task_id,
"streaming": "duplex"
},
"payload": {
"input": {}
}
}
self.ws.send(json.dumps(cmd))
print("已發送 finish-task 指令")
def close(self, ws):
"""主動關閉串連"""
if ws and ws.sock and ws.sock.connected:
ws.close()
print("已主動關閉串連")
def run(self):
"""啟動 WebSocket 用戶端"""
# 佈建要求頭部(鑒權)
header = {
"Authorization": f"bearer {self.api_key}",
"X-DashScope-DataInspection": "enable"
}
# 建立 WebSocketApp 執行個體
self.ws = websocket.WebSocketApp(
self.uri,
header=header,
on_open=self.on_open,
on_message=self.on_message,
on_error=self.on_error,
on_close=self.on_close
)
print("正在監聽 WebSocket 訊息...")
self.ws.run_forever() # 啟動長串連監聽
# 樣本使用方式
if __name__ == "__main__":
API_KEY = os.environ.get("DASHSCOPE_API_KEY") # 如您未將API Key配置到環境變數,請將API_KEY 設定為您的 API Key
SERVER_URI = "wss://dashscope.aliyuncs.com/api-ws/v1/inference/"
client = TTSClient(API_KEY, SERVER_URI)
client.run()標籤
阿里巴巴語音合成服務在實現 SSML 時參考了 W3C SSML 1.0 規範,但在設計上更注重業務適配性。因此,並未完整支援所有標準標籤,而是結合實際使用情境,實現了最具實用價值的標籤集合。
所有使用 SSML 功能的常值內容必須包含在
<speak></speak>標籤內。支援多個
<speak>標籤並列使用(如:<speak></speak><speak></speak>),但不支援嵌套結構(如:<speak><speak></speak></speak>)。編碼時,若標籤內的常值內容包含 XML 特殊字元,需進行相應的字元轉義。常見特殊字元及其轉義形式如下:
"(雙引號) →"'(單引號/撇號) →'&(表示“和”的符號) →&<(小於符號) → <>(大於符號) → >
<speak>:根節點
描述
<speak>標籤是所有 SSML 標籤的根節點,任何使用 SSML 功能的常值內容都必須包含在<speak></speak>標籤之間。文法
<speak>需要使用SSML功能的文本</speak>屬性
屬性名稱
屬性類型
是否必選
描述
voice
String
否
指定發音人(音色)。
優先順序高於介面請求參數
voice指定的發音人。取值範圍:具體的音色,詳情請參見cosyvoice-v2音色。
樣本:
<speak voice="longcheng_v2"> 我是男聲。 </speak>
rate
String
否
指定語速。優先順序高於介面請求參數
speech_rate指定的語速。取值範圍:[0.5,2]之間的小數
預設值:1
大於1表示加快語速
小於1表示減慢語速
樣本:
<speak rate="2"> 我的語速比正常人快。 </speak>
pitch
String
否
指定音高(語調)。優先順序高於介面請求參數
pitch_rate指定的音高(語調)。取值範圍:[0.5,2]之間的小數
預設值:1
大於1表示升高音高
小於1表示降低音高
樣本:
<speak pitch="0.5"> 我的音高卻比別人低。 </speak>
volume
String
否
指定音量。優先順序高於介面請求參數
volume指定的音量。取值範圍:[0,100]之間的整數
預設值:50
大於50表示增大音量
小於50表示減小音量
樣本:
<speak volume="80"> 我的音量也很大。 </speak>
effect
String
否
指定音效。
取值範圍:
robot:機器人音效
lolita:蘿莉音效
lowpass:低通音效
echo:回聲音效
eq:均衡器(進階)
lpfilter:低通濾波器(進階)
hpfilter:高通濾波器(進階)
說明eq、lpfilter、hpfilter是進階音效類型,您可以通過
effectValue參數自訂其具體效果。每個 SSML 標籤僅支援配置一種音效,不允許多個
effect屬性共存。使用音效功能會增加系統延時。
樣本:
<speak effect="robot"> 你喜歡機器人瓦力嗎? </speak>
effectValue
String
否
指定音效(
effect參數)的具體效果。取值範圍:
eq(均衡器):系統預設支援8個頻率等級,對應頻率如下:[“40 Hz”,“100 Hz”, “200 Hz”, “400 Hz”, “800 Hz”, “1600 Hz”, “4000 Hz”, “12000 Hz”]。
每個頻段頻寬均為1.0q。
使用時需通過
effectValue參數指定每個頻段的增益值,該參數為一個由 8 個整數組成的字串,數值範圍為 [-20, 20],數字之間用空格分隔,數值為0表示不調整對應頻率的增益。例如:
effectValue="1 1 1 1 1 1 1 1"lpfilter(低通濾波器):輸入低通濾波器的頻率值。取值為(0, 目標採樣率/2]之間的整數。例如effectValue="800"。hpfilter(高通濾波器):輸入高通濾波器的頻率值。取值為(0, 目標採樣率/2]之間的整數。例如effectValue="1200"。
樣本:
<speak effect="eq" effectValue="1 -20 1 1 1 1 20 1"> 你喜歡機器人瓦力嗎? </speak> <speak effect="lpfilter" effectValue="1200"> 你喜歡機器人瓦力嗎? </speak> <speak effect="hpfilter" effectValue="1200"> 你喜歡機器人瓦力嗎? </speak>
bgm
String
否
為合成的語音添加指定的背景音樂。背景音檔案需儲存在阿里雲 OSS 上(請參見上傳檔案),且所在儲存空間(Bucket)應至少具有公用讀取許可權。
背景音樂URL中若包含 XML 特殊字元(如
&,<,>等),需進行字元轉義處理。音頻要求:
音頻檔案大小無上限,但較大的檔案可能會增加下載耗時;若合成內容的時間長度超過背景音時間長度,背景音將自動迴圈播放以匹配合成音頻長度。
採樣率:16kHz
聲道數:單聲道
檔案格式:WAV
若原始音頻非 WAV 格式,可使用
ffmpeg工具進行轉換:ffmpeg -i 輸入音頻 -acodec pcm_s16le -ac 1 -ar 16000 輸出.wav位元深度:16位
樣本:
<speak bgm="http://nls.alicdn.com/bgm/2.wav" backgroundMusicVolume="30" rate="-500" volume="40"> <break time="2s"/> 陰崖老木蒼蒼煙 <break time="700ms"/> 雨聲猶在竹林間 <break time="700ms"/> 綿蕝固知裨國計 <break time="700ms"/> 綿州風物總堪憐 <break time="2s"/> </speak>
重要您需要對上傳的音頻著作權承擔相應的法律責任。
backgroundMusicVolume
String
否
指定背景音樂的音量。和
backgroundMusicVolume屬性搭配使用。標籤關係
<speak>標籤可以包含文本和其他標籤:
更多樣本
空屬性
<speak> 需要調用SSML標籤的文本 </speak>屬性群組合(空格分隔)
<speak rate="200" pitch="-100" volume="80"> 所以放在一起,我的聲音是這樣的。 </speak>
<break>:控制停頓時間
描述
在語音合成過程中添加一段靜默時間,類比自然說話中的停頓效果。支援秒(s)或毫秒(ms)單位設定。該標籤是可選標籤。
文法
# 空屬性 <break/> # 帶time屬性 <break time="string"/>屬性
說明使用無屬性的<break>標籤時,停頓時間長度為“1s”。
屬性名稱
屬性類型
是否必選
描述
time
String
否
以秒/毫秒為單位設定停頓的時間長度 (如“2s”、“50ms”)。
取值範圍:
以秒(s)為單位,取值範圍為[1, 10]之間的整數
以毫秒(ms)為單位,取值範圍為[50, 10000]之間的整數
樣本:
<speak> 請閉上眼睛休息一下<break time="500ms"/>好了,請睜開眼睛。 </speak>
重要當連續使用多個
<break>標籤時,總的停頓時間長度為各個標籤指定時間的累加。若總時間長度超過 10 秒,則僅生效前 10 秒。如下所示,該段 SSML 中
<break>標籤累計時間長度為 15 秒,超過 10 秒限制,最終停頓時間長度將被截斷為 10 秒:<speak> 請閉上眼睛休息一下<break time="5s"/><break time="5s"/><break time="5s"/>好了,請睜開眼睛。 </speak>標籤關係
<break>是空標籤,不能包含任何標籤。
<sub>:替換文本
描述
將某段文本替換為指定的更適合朗讀的文本。例如將 “W3C” 讀成 “網路通訊協定標準”。該標籤是可選標籤。
文法
<sub alias="string"></sub>屬性
屬性名稱
屬性類型
是否必選
描述
alias
String
是
將某段文本替換為更適合朗讀的文本。
樣本:
<speak> <sub alias="網路通訊協定標準">W3C</sub> </speak>標籤關係
<sub>標籤僅可以包括文本。
<phoneme>:指定發音(拼音/音標)
描述
控制某段文本的具體發音方式,中文可用拼音,英文可用音標(如 CMU),適用於需要精準發音的情境。該標籤是可選標籤。
文法
<phoneme alphabet="string" ph="string">文本</phoneme>屬性
屬性名稱
屬性類型
是否必選
描述
alphabet
String
是
指定發音類型:拼音(對應中文)或音標(對應英文)。
取值範圍:
"py":拼音
"cmu":音標,參見The CMU Pronouncing Dictionary
ph
String
是
指定具體的拼音或音標:
字與字的拼音用空格分隔,拼音的數目必須與字數一致。
每個拼音由發音部分和音調組成,其中音調為
1到5的整數,5表示輕聲。樣本:
<speak> 去<phoneme alphabet="py" ph="dian3 dang4 hang2">典當行</phoneme>把這個玩意<phoneme alphabet="py" ph="dang4 diao4">當掉</phoneme> </speak> <speak> How to spell <phoneme alphabet="cmu" ph="S AY N">sin</phoneme>? </speak>
標籤關係
<phoneme>標籤僅包括文本。
<soundEvent>:插入一段外部聲音(鈴聲、貓叫等)
描述
支援在語音中插入音效檔案,如提示音、環境音等,增強語音表達的豐富性。該標籤是可選標籤。
文法
<soundEvent src="URL"/>屬性
屬性名稱
屬性類型
是否必選
描述
src
String
是
設定外部音頻URL。
音頻檔案需儲存在阿里雲 OSS 上(請參見上傳檔案),且所在儲存空間(Bucket)應至少具有公用讀取許可權。URL中若包含 XML 特殊字元(如
&,<,>等),需進行字元轉義處理。音頻要求:
採樣率:16kHz
聲道數:單聲道
檔案格式:WAV
若原始音頻非 WAV 格式,可使用
ffmpeg工具進行轉換:ffmpeg -i 輸入音頻 -acodec pcm_s16le -ac 1 -ar 16000 輸出.wav
檔案大小:不超過2MB
位元深度:16位
樣本:
<speak> 一匹馬受了驚嚇<soundEvent src="http://nls.alicdn.com/sound-event/horse-neigh.wav"/>人們四散躲避 </speak>
重要您需要對上傳的音頻著作權承擔相應的法律責任。
標籤關係
<soundEvent>是空標籤,不可以包含任何標籤。
<say-as>:設定文本的讀法(數字、日期、電話號碼等)
描述
告訴大模型文本是什麼類型,並按該類型的常規讀法進行朗讀。該標籤是可選標籤。
文法
<say-as interpret-as="string">文本</say-as>屬性
屬性名稱
屬性類型
是否必選
描述
interpret-as
String
是
指示出標籤內文本的資訊類型。
取值範圍:
cardinal:按整數或小數的常見讀法朗讀
digits:按數字逐個讀出(如:123 → 一二三)
telephone:按電話號碼的常用方式讀出
name:按人名的常規讀法朗讀
address:按地址的常見方式讀出
id:適用於賬戶名、暱稱等,按常規讀法處理
characters:將標籤內的文本按字元一一讀出
punctuation:將標籤內的文本按標點符號的方式讀出來
date:按日期格式的常見讀法朗讀
time:按時間格式的常見方式讀出
currency:按金額的常見讀法處理
measure:按計量單位的常見方式讀出
各<say-as>類型支援範圍
cardinal
格式
樣本
中文輸出
說明
數字串
145
一百四十五
整數輸入範圍:20位以內的正負整數,[-99999999999999999999,99999999999999999999]。
小數輸入範圍:對小數點後小數的位元沒有特殊限制,建議不超過10位。
負號+數字串
-145
負一百四十五
以逗號分隔3位元字串
10,000
一萬
負號+以逗號分隔3位元字串
-10,124
負一萬一百二十四
數字串+小數點+2個零
10.00
十
負號+數字串+小數點+2個零
-110.00
負一百一十
數字串+小數點+數字串
79.090
七十九點零九零
負號+數字串+小數點+數字串
-79.001
負七十九點零零一
格式
樣本
英文輸出
說明
數字串
145
one hundred forty five
整數輸入範圍:13位以內的正負整數,[-999999999999,999999999999]。
小數輸入範圍:對小數點後小數的位元沒有特殊限制,建議不超過10位。
以零開頭的數字串
0145
one hundred forty five
負號+數字串
-145
minus hundred forty five
以逗號分隔三位元字串
60,000
sixty thousand
負號+以逗號分隔三位元字串
-208,000
minus two hundred eight thousand
數字串+小數點+零
12.00
twelve
數字串+小數點+數字串
12.34
twelve point three four
以逗號分隔三位元字串+小數點+數字串
1,000.1
one thousand point one
負號+數字串+小數點+數字串
-12.34
minus twelve point three four
負號+以逗號分隔三位元字串+小數點+數字串
-1,000.1
minus one thousand point one
(以逗號分隔三位)數字串+連詞符+(以逗號分隔三位)數字
1-1,000
one to one thousand
其他預設讀法
012.34
twelve point three four
無
1/2
one half
-3/4
minus three quarters
5.1/6
five point one over six
-3 1/2
minus three and a half
1,000.3^3
one thousand point three to the power of three
3e9.1
three times ten to the power of nine point one
23.10%
twenty three point one percent
digits
格式
樣本
中文輸出
說明
數字串
129090909
一二九零九零九零九
對數字串的長度沒有特殊限制,建議不超過20位。
當數字串超過10位時,每個數字後插入停頓。
格式
樣本
英文輸出
說明
數字串
12034
one two zero three four
對數字串的長度沒有特殊限制,建議不超過20位。
當數字串以空格或連詞符分組時,分組之間會插入逗號而產生適當停頓,支援最長5個分組。
數字串+空格或連詞符+數字串+空格或連詞符+數字串+空格或連詞符+數字串
1-23-456 7890
one, two three, four five six, seven eight nine zero
telephone
格式
樣本
中文輸出
說明
有線電話號
4930286
四九三 零二八六
支援7~8位有線電話號,支援空格和“-”作為分隔字元。
其中,7位有線電話號支援“3-4”的數字分隔方式;8位有線電話號支援“4-4”的數字分隔方式。
493 0286
四九三 零二八六
493-0286
四九三 零二八六
62552560
六二五五 二五六零
6255 2560
六二五五 二五六零
6255-2560
六二五五 二五六零
有線電話號+分機號
4930286-109
四九三 零二八六 轉么零九
支援1~4位分機號。
4930286轉109
四九三 零二八六 轉么零九
4930286分機109
四九三 零二八六 分機么零九
4930286分機號109
四九三 零二八六 分機號么零九
區號+有線電話號
01062552560
零么零 六二五五 二五六零
支援區號:010、02x、03xx、04xx、05xx、07xx、08xx、09xx。
010 62552560
零么零 六二五五 二五六零
010 6255 2560
零么零 六二五五 二五六零
010 6255-2560
零么零 六二五五 二五六零
010-62552560
零么零 六二五五 二五六零
010-6255-2560
零么零 六二五五 二五六零
(010)62552560
零么零 六二五五 二五六零
03198907098
零三么九 八九零 七零九八
0319-8907098
三么九 八九零 七零九八
區號+有線電話號+分機號
010 62552560-109
零么零 六二五五 二五六零 轉么零九
無
010-62552560-109
零么零 六二五五 二五六零 轉么零九
(010)62552560-109
零么零 六二五五 二五六零 轉么零九
(010)62552560轉109
零么零 六二五五 二五六零 轉么零九
(010)62552560分機109
零么零 六二五五 二五六零 分機么零九
(010)62552560分機號109
零么零 六二五五 二五六零 分機號么零九
國家代碼+區號+有線電話號
86-010-62791627
八六 零么零 六二七九 么六二七
支援國家代碼:86、 (86)、+86、(+86)、0086。並統一讀為“八六”。
(86)10-62791627
八六 么零 六二七九 么六二七
+86-010-62791627
八六 零么零 六二七九 么六二七
0086-10-62791627
八六 么零 六二七九 么六二七
(+86)-10-6279 1627
八六 么零 六二七九 么六二七
國家代碼+區號+有線電話號+分機號
(86)21-58118818-207
八六 二么 五八么么 八八么八 轉二零七
無
(86)021-5811-8818-207
八六 零二么 五八么么 八八么八 轉二零七
(86)021-58118818轉207
八六 零二么 五八么么 八八么八 轉二零七
(86)21-5811-8818分機207
八六 二么 五八么么 八八么八 分機二零七
+86-021-58118818分機號207
八六 零二么 五八么么 八八么八分機號二零七
手機號
139 0000 5678
么三九 零零零零 五六七八
支援11位手機號,支援3-3-5、3-4-4兩種數字分隔方式
139-000-05678
么三九 零零零 零五六七八
139 000 05678
么三九 零零零 零五六七八
國家代碼+手機號
+86-13900005678
八六 么三九 零零零零 五六七八
無
(+86)-139-0000-5678
八六 么三九 零零零零 五六七八
+8613900005678
八六 么三九 零零零零 五六七八
0086-139 000 05678
八六 么三九 零零零 零五六七八
服務號
123
么二三
支援常用的服務號。
支援以400/800開頭的10位服務號,支援以“3-3-4”的數字分隔方式。
支援以12530/17951/12593開頭的16位號碼。
95678
九五六七八
4008110510
四零零 八么么 零五么零
800-810-8888
八零零 八么零 八八八八
1253013520638377
么二五三零 么三五 二零六三 八三七七
其他
(86)(21)9899-80800-0909
八六 二么 九八九九 八零八零零 零九零九
支援“數字串+分隔字元(左右括弧、-)”方式。
格式
樣本
英文輸出
說明
數字串
12034
one two oh three four
對數字串的長度沒有特殊限制,建議不超過20位。當數字串以空格或連詞符分組時,分組之間會插入逗號而產生適當停頓,支援最長5個分組。
數字串+空格或連詞符+數字串+空格或連詞符+數字串
1-23-456 7890
one, two three, four five six, seven eight nine oh
加號+數字串+空格或連詞符+數字串
+43-211-0567
plus four three, two one one, oh five six seven
左括弧+數字串+右括弧+空格+數字串+空格或連詞符+數字串
(21) 654-3210
(two one) six five four, three two one oh
address
格式
樣本
中文輸出
說明
常用地址格式
元和鎮嘉元30-9
元和鎮嘉元三十杠九
支援常用地址格式。此處地址指標準的郵寄地址。
市台路388弄1107-1108號
市台路三八八弄么么零七杠么么零八號
華潤二十四城六期錦雲府3-1-3205
華潤二十四城六期錦雲府三杠一杠三二零五
聖華名都大廈2幢2006室
聖華名都大廈二幢二零零六室
五常街道庭院5幢4單元201
五常街道庭院五幢四單元二零么
芙蓉江路150弄19號
芙蓉江路么五零弄十九號
英文文本不支援該標籤。
id
格式
樣本
輸出
說明
字串
dell0101
D E L L 零 一 零 一
大小寫英文字元、阿拉伯數字0~9、底線。
輸出的空格表示每個字元之間插入停頓,即字元一個一個地讀。
myid_1998
M Y I D 底線 一 九 九 八
AiTest
A I T E S T
英文文本該標籤功能同標籤characters。
characters
格式
樣本
中文輸出
說明
字串
ISBN 1-001-099098-1
I S B N 一 杠 零 零 一 杠 零 九 九 零 九 八 杠 一
支援中文漢字、大小寫英文字元、阿拉伯數字0~9以及部分全形和半形字元。
輸出的空格表示每個字元之間插入停頓,即字元一個一個地讀。標籤內的文本如果包含XML的特殊字元,需要做字元轉義。
x10b2345_u
x 一 零 b 二 三 四 五 底線 u
v1.0.1
v 一 點 零 點 一
版本號碼2.0
版本號碼二 點 零
蘇M MA000
蘇M M A 零 零 零
空中巴士A330
空中巴士A 三 三 零
型號s01 s02和s03
型號s 零 一 s 零二 和s 零 三
空中巴士A330
空中巴士A 三 三 零
αβγ
阿爾法 貝塔 伽瑪
格式
樣本
英文輸出
說明
字串
*b+3$.c-0'=α
asterisk B plus three dollar dot C dash zero apostrophe equals alpha
支援中文漢字、大小寫英文字元、阿拉伯數字0~9以及部分全形和半形字元。
輸出的空格表示每個字元之間插入停頓,即字元一個一個地讀。
標籤內的文本如果包含XML的特殊字元,需要做字元轉義。
punctuation
格式
樣本
中文輸出
說明
標點符號
…
省略符號
支援常見中英文標點。輸出的空格表示每個字元之間插入停頓,即字元一個一個地讀。
標籤內的文本如果包含XML的特殊字元,需要做字元轉義。
……
省略符號
!"#$%&
歎號 雙引號 井號 dollar 百分比符號 and
‘()*+
單引號 左括弧 右括弧 星號 加號
,-./:;
逗號 杠 點 斜杠 冒號 分號
<=>?@
小於 等號 大於 問號 at
[\]^_
左方括弧 反斜線 右方括弧 脫字元 底線
英文文本該標籤功能同標籤characters。
date
格式
樣本
中文輸出
說明
xx年
71年
七一年
支援2位和4位年份。其中:
2位年份支援60年~99年、00年~09年、10年~19年。
4位年份支援1000年~1999年、2000年~2099年。
04年
零四年
19年
一九年
1011年
一零一一年
1998年
一九九八年
2008年
二零零八年
xx年xx月
98年4月
九八年四月
當月份為1到9月時,支援開頭帶“0”和不帶“0”兩種寫法。例如“1908年4月”和“1908年04月”。
1998年04月
一九九八年四月
08年8月
零八年八月
2008年8月
二零零八年八月
xx年xx月xx日xx年xx月xx號
98年4月23日
九八年四月二十三日
當日期為1到9日時,支援開頭帶“0”和不帶“0”兩種寫法。例如“1908年4月8日”和“1908年04月08日”。
1998年04月23日
一九九八年四月二十三日
08年8月8號
零八年八月八號
2008年08月08號
二零零八年八月八號
xx年xx月xx日xx年xx月xx號
98年4月23日
九八年四月二十三日
當日期為1到9日時,支援開頭帶“0”和不“0”兩種寫法。例如“1908年4月8日”和“1908年04月08日”。
1998年04月23日
一九九八年四月二十三日
08年8月8號
零八年八月八號
2008年08月08號
二零零八年八月八號
xx月xx號
3月20日
三月二十日
無
08月07號
八月七號
年月縮寫
2018/08
二零一八年八月
支援“/”、“-”、“.”作為縮寫的分隔字元。
2018-08
二零一八年八月
2018.08
二零一八年八月
年月日縮寫
2018/08/08
二零一八年八月八日
2018-8-8
二零一八年八月八日
2018.08.08
二零一八年八月八日
xx年xx月xx日~xx年xx月xx日xx年xx月xx號~xx年xx月xx號
04年9月1日~30日
零四年九月一日至三十日
支援“~”、“-”作為“至”的縮寫標誌。
2004年09月01號-2008年06月08號
二零零四年九月一號至二零零八年六月八號
xx年xx月xx日~xx日xx年xx月xx號~xx號
04年9月1日~30日
零四年九月一日至三十日
2004年09月01號-2008年06月08號
二零零四年九月一號至二零零八年六月八號
xx年xx月~xx年xx月
01年04月~10年04月
零一年四月至一零年四月
2001年04月~2010年04月
二零零一年四月至二零一零年四月
xx月xx日~xx月xx日xx月xx號~xx月xx號
10月1日~10月7日
十月一日至十月七日
10月01號~10月07號
十月一號至十月七號
xx月xx日~xx日xx月xx號~xx號
10月1日~7日
十月一日至七日
10月01號~07號
十月一號至七號
年月日縮寫~年月日縮寫
2018/03/03~2019/01/01
二零一八年三月三日至二零一九年一月一日
支援“/”、“.”作為縮寫的分隔字元,支援“~”、“-”作為“至”的縮寫標誌。
1997.9.9~1998.9.9
一九九七年九月九日至一九九八年九月九日
月日縮寫~月日縮寫
10/20~10/31
十月二十日至十月三十一日
xx~xx月xx月~xx月
1~10月
一至十月
1月~10月
一月至十月
月日年縮寫
10/20/2018
二零一八年十月二十日
僅支援4位的年份,僅支援“/”作為日期的分隔字元,僅支援“月/日/年”的書寫方式。
格式
樣本
英文輸出
說明
四位元字/兩位元字或四位元字-兩位元字
2000/01
two thousand, oh one
跨年度。
1900-01
nineteen hundred, oh one
2001-02
twenty oh one, oh two
2019-20
twenty nineteen, twenty
1998-99
nineteen ninety eight, ninety nine
1999-00
nineteen ninety nine, oh oh
以1或2開頭的四位元字
2000
two thousand
四位元字年份。
1900
nineteen hundred
1905
nineteen oh five
2021
twenty twenty one
星期幾-星期幾
或
星期幾~星期幾
或
星期幾&星期幾
mon-wed
monday to wednesday
星期幾的範圍標籤內的文本如果包含XML的特殊字元,需要做字元轉義。
tue~fri
tuesday to friday
sat&sun
saturday and sunday
DD-DD MMM, YYYY
或
DD~DD MMM, YYYY
或
DD&DD MMM, YYYY
19-20 Jan, 2000
the nineteen to the twentieth of january two thousand
DD表示兩位元字日期,MMM表示月份的三字母縮寫或完整單詞,YYYY表示以1或2開頭的四位元字年份。
01 ~ 10 Jul, 2020
the first to the tenth of july twenty twenty
05&06 Apr, 2009
the fifth and the sixth of april two thousand nine
MMM DD-DD
或
MMM DD~DD
或
MMM DD&DD
Feb 01 - 03
feburary the first to the third
MMM表示月份的三字母縮寫或完整單詞,DD表示兩位元字日期。
Aug 10~20
august the tenth to the twentieth
Dec 11&12
december the eleventh and the twelfth
MMM-MMM
或
MMM~MMM
或
MMM&MMM
Jan-Jun
january to june
MMM表示月份的三字母縮寫或完整單詞。
jul ~ dec
july to december
sep&oct
september and october
YYYY-YYYY
或
YYYY~YYYY
1990 - 2000
nineteen ninety to two thousand
YYYY表示以1或2開頭的四位元字年份。
2001~2021
two thousand one to twenty twenty one
WWW DD MMM YYYY
Sun 20 Nov 2011
sunday the twentieth of november twenty eleven
WWW表示星期幾的三字母縮寫或完整單詞,DD表示兩位元字日期,MMM表示月份的三字母縮寫或完整單詞,MM表示兩位元字月份(或三字母縮寫或完整單詞),YYYY表示以1或2開頭的四位元字年份。
WWW DD MMM
Sun 20 Nov
sunday the twentieth of november
WWW MMM DD YYYY
Sun Nov 20 2011
sunday november the twentieth twenty eleven
WWW MMM DD
Sun Nov 20
sunday november the twentieth
WWW YYYY-MM-DD
Sat 2010-10-01
aturday october the first twenty ten
WWW YYYY/MM/DD
Sat 2010/10/01
saturday october the first twenty ten
WWW MM/DD/YYYY
Sun 11/20/2011
sunday november the twentieth twenty eleven
MM/DD/YYYY
11/20/2011
november the twentieth twenty eleven
YYYY
1998
nineteen ninety eight
其他預設讀法
10 Mar, 2001
the tenth of march two thousand one
無
10 Mar
the tenth of march
Mar 2001
march two thousand one
Fri. 10/Mar/2001
friday the tenth of march two thousand one
Mar 10th, 2001
march the tenth two thousand one
Mar 10
march the tenth
2001/03/10
march the tenth two thousand one
2001-03-10
march the tenth two thousand one
2000s
two thousands
2010's
twenty tens
1900's
nineteen hundreds
1990s
nineteen nineties
time
格式
樣本
中文輸出
說明
時刻
12:00
十二點
支援常用時間和時間範圍格式。
12:00:00點
十二點
10:20分
十點二十分
10:20:30
十點二十分三十秒
09:18:14
九點十八分十四秒
時刻~時刻
11:00~12:00
十一點到十二點
09:00-14:00
九點到十四點
11:00~11:30
十一點到十一點三十分
11:00-12:18
十一點到十二點十八分
10:30~11:00
十點三十分到十一點
09:28-10:00
九點二十八分到十點
10:20~11:20
十點二十分到十一點二十分
06:00~08:00
六點到八點
上午10:20~下午13:30
上午十點二十分到下午十三點三十分
時間縮寫
5:00 am
淩晨五點整
5:30 am
淩晨五點半
5:20:12 am
淩晨五點二十分十二秒
7:00 am
上午七點整
7:30 AM
上午七點半
7:20:12 a.m.
上午七點二十分十二秒
07:08:12 A.M.
上午七點零八分十二秒
5:00 pm
下午五點整
5:30 PM
下午五點半
5:20:12 p.m.
下午五點二十分十二秒
05:09:12 P.M.
下午五點零九分十二秒
9:00 pm
晚上九點整
9:30 pm
晚上九點半
9:20:12 PM
晚上九點二十分十二秒
9:02:12 P.M.
晚上九點零二分十二秒
12:00 pm
中午十二點整
12:30 p.m.
中午十二點半
12:20:12 PM
中午十二點二十分十二秒
格式
樣本
英文輸出
說明
HH:MM AM或PM
09:00 AM
nine A M
HH表示一或兩位元字小時,MM表示兩位元字分鐘,AM/PM表示上/下午。
09:03 PM
nine oh three P M
09:13 p.m.
nine thirteen p m
HH:MM
21:00
twenty one hundred
HHMM
100
one oclock
時刻-時刻
8:00 am - 05:30 pm
eight a m to five p m
支援常見時間格式和範圍。
7:05~10:15 AM
seven oh five to ten fifteen A M
09:00-13:00
nine oclock to thirteen hundred
currency
格式
樣本
中文輸出
說明
數字+金額標識符
12.00 RMB
十二人民幣
支援AUD(澳幣) 、CAD(加元)、 HKD(港幣)、JPY(日元)、USD(美元)、CHF(瑞士法郎)、NOK(挪威克朗)、SEK(瑞典克朗)、GBP(英鎊)、 RMB(人民幣)、CNY(元)和EUR(歐元)。
支援的數字格式包括:整數、小數以及以逗號分隔的國際寫法。
12.50 RMB
十二點五零人民幣
12,000,000 RMB
一千二百萬人民幣
12,000,000.00 RMB
一千二百萬人民幣
12,000.35 RMB
一萬兩千點三五人民幣
金額標識符+數字
$12
十二美元
支援 CAD(加元)、 $(美元)、Fr(法郎)、kr(丹麥幣)、 £(英鎊)、¥(元)和 €(歐元)。
支援的數字格式包括:整數、小數以及以逗號分隔的國際寫法。
$12.00
十二美元
$12.12
二點一二美元
$12,000
一萬兩千美元
$12,000.00
一萬兩千美元
$12,000.99
一萬兩千點九九美元
其他預設讀法
1213
一千二百一十三
無
1213 KML
一千二百一十三K M L
1213.00 KML
一千二百一十三K M L
1213.9 KML
一千二百一十三點九K M L
1,000 KML
一千K M L
1,000.00 KML
一千K M L
1,000.98 KML
一千點九八K M L
12,000
一萬兩千
格式
樣本
英文輸出
說明
數字+金額識別符
1.00 RMB
one yuan
支援的數字格式:整數、小數以及以逗號分隔的國際寫法。
支援的金額識別符:
CN¥ (yuan)
CNY (yuan)
RMB (yuan)
AUD (australian dollar)
CAD (canadian dollar)
CHF (swiss franc)
DKK (danish krone)
EUR (euro)
GBP (british pound)
HKD (Hong Kong(China) dollar)
JPY (japanese yen)
NOK (norwegian krone)
SEK (swedish krona)
SGD (singapore dollar)
USD (united states dollar)
2.02 CNY
two point zero two yuan
1,000.23 CN¥
one thousand point two three yuan
1.01 SGD
one singapore dollar and one cent
2.01 CAD
two canadian dollars and one cent
3.1 HKD
three hong kong dollars and ten cents
1,000.00 EUR
one thousand euros
金額識別符+數字
US$ 1.00
one US dollar
支援的數字格式:整數、小數以及以逗號分隔的國際寫法。
支援的金額識別符:
US$ (US dollar)
CA$ (Canadian dollar)
AU$ (Australian dollar)
SG$ (Singapore dollar)
HK$ (Hong Kong dollar)
C$ (Canadian dollar)
A$ (Australian dollar)
$ (dollar)
£ (pound)
€ (euro)
CN¥ (yuan)
CNY (yuan)
RMB (yuan)
AUD (australian dollar)
CAD (canadian dollar)
CHF (swiss franc)
DKK (danish krone)
EUR (euro)
GBP (british pound)
HKD (Hong Kong(China) dollar)
JPY (japanese yen)
NOK (norwegian krone)
SEK (swedish krona)
SGD (singapore dollar)
USD (united states dollar)
$0.01
one cent
JPY 1.01
one japanese yen and one sen
£1.1
one pound and ten pence
€2.01
two euros and one cent
USD 1,000
one thousand united states dollars
數字+量詞+金額識別符
或
金額識別符+數字+量詞
1.23 Tn RMB
one point two three trillion yuan
支援的量詞格式包括:
thousand
million
billion
trillion
Mil (million)
mil (million)
Bil (billion)
bil (billion)
MM (million)
Bn (billion)
bn (billion)
Tn (trillion)
tn (trillion)
K(thousand)
k (thousand)
M (million)
m (million)
$1.2 K
one point two thousand dollars
measure
格式
樣本
中文輸出
說明
數字+中文單位
2片
兩片
支援常見中文單位及單位縮寫。
120公頃
一百二十公頃
100多毫克
一百多毫克
100來米
一百來米
100餘人
一百餘人
1厘米20毫米
一厘米二十毫米
120.00平方公裡
一百二十平方公裡
數字+單位縮寫
120.56 cm²
一百二十點五六平方厘米
120 ㎡ 56 cm²
一百二十平方米五十六平方厘米
100 m 12 cm 6 mm
一百米十二厘米六毫米
範圍
10~15 kg
十至十五千克
10.24~789.82畝
十點二四至七百八十九點八二畝
10米~15米
十米至十五米
10.24 cm~19.08 cm
十點二四厘米至十九點零八厘米
數字+單位+"/"+單位
10元/斤
十元每斤
199~299元/件
一百九十九至二百九十九元每件
299.99元/g~399.99元/g
二百九十九點九九元每克至三百九十九點九九元每克
其他預設讀法
12紮
十二紮
30 rm
三十r m
4萬萬同胞
四萬萬同胞
12.897微克
十二點八九七微克
格式
樣本
英文輸出
說明
數字+計量單位
1.0 kg
one kilogram
支援的數字格式:整數、小數以及以逗號分隔的國際寫法。
支援常見單位縮寫。
1,234.01 km
one thousand two hundred thirty four point zero one kilometres.
計量單位
mm2
square millimetre
<say-as>常見符號讀法如下表所示。
符號
中文讀法
英文讀法
!
歎號
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
<say-as>常見計量單位如下表所示。
格式
類別
中文樣本
英文樣本
縮寫
長度
nm(納米)、μm(微米)、 mm(毫米)、cm(厘米)、m(米)、km(千米)、ft(英尺)、in(英寸)
nm (nanometre), μm (micrometre), mm (millimetre), cm (centimetre), m (metre), km (kilometre), ft (foot), in (inch)
面積
cm²(平方厘米)、㎡(平方米)、km²(平方千米)、SqFt(平方英尺)
cm² (square centimetre), ㎡ (square metre), km2 (square kilometre), SqFt (square foot)
體積
cm³(立方厘米)、m³(立方米)、km³(立方千米)、mL(毫升)、L(升)、gallon(加侖)
cm³ (cubic centimetre), m³ (cubic metre), km3 (cubic kilometre), mL (millilitre), L (millilitre), gal (gallon)
重量
μg(微克)、mg(毫克)、g(克)、kg(千克)
μg (microgram), mg (microgram), g (gram), kg (kilogram)
時間
min(分)、sec(秒)、ms(毫秒)
min (minute), sec (second), ms (millisecond)
電磁
μA(微安)、mA(毫安)、Ω(歐姆)、Hz(赫茲)、kHz(千赫茲)、MHz(兆赫茲)、GHz(吉赫茲)、V(伏)、kV(千伏)、kWh(千瓦時)
μA (microamp), mA (milliamp), Hz (hertz), kHz (kilohertz), MHz (megahertz), GHz (gigahertz), V (volt), kV (kilovolt), kWh (kilowatt hour)
聲音
dB(分貝)
dB (decibel)
氣壓
Pa(帕)、kPa(千帕)、Mpa(兆帕)
Pa (pascal), kPa (kilopascal), MPa (megapascal)
其他常見單位
支援不限於上述類別的中文單位,例如“米”、“秒”、“美元”、“毫升每瓶”等。以及中文量詞,例如“架”、“場”、“頭”、“部”、“盆”等。
支援不限於上述類別的計量單位,例如 tsp (teaspoon), rpm (round per minute), KB (kilobyte), mmHg (milimetre of mercury) 等。
標籤關係
<say-as>標籤可以包括文本及<vhml/>。
樣本
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>