通过WebSocket连接访问Sambert语音合成服务

本文介绍如何通过WebSocket连接访问Sambert语音合成服务。

DashScope SDK目前仅支持JavaPython。若想使用其他编程语言开发Sambert语音合成应用程序,可以通过WebSocket连接与服务进行通信。

WebSocket是一种支持全双工通信的网络协议。客户端和服务器通过一次握手建立持久连接,双方可以互相主动推送数据,因此在实时性和效率方面具有显著优势。

对于常用编程语言,有许多现成的WebSocket库和示例可供参考,例如:

  • Go:gorilla/websocket

  • PHP:Ratchet

建议您先了解WebSocket的基本原理和技术细节,再参照本文进行开发。

前提条件

开通模型服务并获取API Key,建议您API Key配置到环境变量

流程简述

客户端和服务端交互流程如下:

  1. 建立连接。

  2. 发送并接收消息。

    1. 客户端发送run-task指令,该指令中包含待合成语音的文本。

    2. 服务端收到run-task指令后返回task-started事件。

    3. 服务端持续返回合成的音频流和result-generated事件。

    4. 服务端返回task-finished事件,标志着语音合成任务结束。

  3. 关闭连接。

image

流程详情

对于不同的编程语言,尽管实现细节可能有所不同,但基本流程是一致的。

一、建立连接

通过调用工具库,将请求头和URL传入以建立WebSocket连接。

请求头中需添加如下鉴权信息:

{
    "Authorization": "bearer <your-dashscope-api-key>", // 将<your-dashscope-api-key>替换成您自己的API-KEY
    "user-agent": "your-platform-info", //可选
    "X-DashScope-WorkSpace": workspace, // 可选
    "X-DashScope-DataInspection": "enable"
}

WebSocket URL固定如下:

wss://dashscope.aliyuncs.com/api-ws/v1/inference

二、发送并接收消息

什么是指令和事件?

客户端发送给服务端的消息叫做指令,指令以Text Frame方式发送,用于控制语音合成任务的起止和标识任务边界。

服务端返回给客户端的消息叫做事件,事件代表不同的处理阶段。

指令和事件都是JSON格式,由headerpayload这两部分组成。

  • header包含基础信息,格式较为统一。

    指令中的header

    所有指令的header格式统一。

    header示例:

    {
        "header": {
            "action": "run-task",
            "task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx", // 随机uuid
            "streaming": "out"
        }
    }

    header参数:

    参数

    类型

    是否必选

    说明

    header

    请求头

    -

    -

    header.action

    String

    指令类型,固定为run-task。用法参见下文。

    header.task_id

    String

    当次任务ID,随机生成32位唯一ID。

    32位通用唯一识别码(UUID),由32个随机生成的字母和数字组成。可以带横线(如 "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx")或不带横线(如 "2bf83b9abaeb4fda8d9axxxxxxxxxxxx")。大多数编程语言都内置了生成UUIDAPI,例如Python:

    import uuid
    
    def generateTaskId(self):
        # 生成随机UUID
        return uuid.uuid4().hex

    header.streaming

    String

    固定字符串:"out"

    事件中的header

    task-failed外,所有事件的header格式统一。

    header示例:

    {
        "header": {
            "task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx",
            "event": "task-started",
            "attributes": {}
        }
    }

    header参数:

    参数

    类型

    说明

    header

    请求头

    -

    header.event

    String

    事件类型

    • task-started

    • result-generated

    • task-finished

    • task-failed

    详细说明参见下文。

    header.task_id

    String

    客户端生成的task_id

  • payload包含基础信息外的其他信息。不同指令或者事件的payload格式可能不同。

消息发送与接收流程

1、发送run-task指令:开启语音合成任务

该指令用于开启语合成任务。

示例:

{
    "header": {
        "action": "run-task",
        "task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx",
        "streaming": "out"
    },
    "payload": {
        "model": "sambert-zhichu-v1",
        "task_group": "audio",
        "task": "tts",
        "function": "SpeechSynthesizer",
        "input": {
            "text": "床前明月光,"                //待合成文本
        },
        "parameters": {
            "text_type": "PlainText",
            "format": "mp3",                    //音频格式
            "sample_rate": 16000,               //采样率
            "volume": 50,                       //音量
            "rate": 1,                          //语塞
            "pitch": 1,                         //音调
            "word_timestamp_enabled": true,     //是否开启词时间戳
            "phoneme_timestamp_enabled": true   //是否开启音素时间戳
        }
    }
}

payload参数说明:

参数

类型

是否必选

说明

payload.task_group

String

固定字符串:"audio"。

payload.task

String

固定字符串:"tts"。

payload.function

String

固定字符串:"SpeechSynthesizer"。

payload.model

String

模型名称。支持的模型请参考模型列表

payload.input.text

String

待合成文本,要求采用UTF-8编码且不能为空,一次性合成最高一万字符,其中每个汉字、英文、标点符号均按照1个字计算,支持SSML格式。SSML标记语言使用,请点击SSML标记语言介绍

payload.parameters

text_type

String

固定字符串:“PlainText”

format

String

音频编码格式,支持pcm/wav/mp3格式。

sample_rate

Integer

合成音频的采样率。建议使用模型默认采样率(参考模型列表),如果不匹配,服务会进行必要的升降采样处理。

volume

Integer

音量,取值范围:0~100。默认值:50。

rate

Float

合成音频的语速,取值范围:0.5~2。

  • 0.5:表示默认语速的0.5倍速。

  • 1:表示默认语速。默认语速是指模型默认输出的合成语速,语速会依据每个发音人略有不同,约每秒钟4个字。

  • 2:表示默认语速的2倍速。

默认值:1.0

pitch

Float

合成音频的语调,取值范围:0.5~2。

默认值:1.0。

2、接收task-started事件:语音合成任务已开启

客户端成功发送run-task指令后,服务端返回task-started事件。

task-started事件的payload没有内容。

示例:

{
    "header": {
        "task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx",
        "event": "task-started",
        "attributes": {}
    },
    "payload": {}
}
3、接收音频流

服务端在返回task-started事件后,会持续返回音频流。

Sambert语音合成中,音频通过二进制通道以数据流方式分帧下发。下发的所有音频可以合成为一个完整的音频文件,也可以通过支持流式播放的播放器实时播放。

  • 若要将所有音频合成为一个完整的音频文件,需使用追加模式写入同一个文件。

  • 若要流式播放音频,需使用支持流式播放的音频播放器,否则无法播放。

    支持流式播放的播放器包括:FFmpeg、PyAudio(Python)、AudioFormat(Java)、MediaSource(JavaScript)等。

  • 在使用 WAV/MP3 格式合成音频时,由于文件按流式合成,因此仅在第一帧中包含当前任务的文件头信息。

4、接收result-generated事件:获取时间戳信息

服务器在返回音频流的同时,也会返回result-generated事件。

如果模型支持时间戳功能并且选择开启该功能,result-generated事件中会附带时间戳信息。

  • 开启词时间戳:将run-task指令中的word_timestamp_enabled设置为true。

  • 开启音素时间戳:将run-task指令中的phoneme_timestamp_enabled设置为true。

示例:

{
    "header": {
        "task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx",
        "event": "result-generated",
        "attributes": {}
    },
    "payload": {
        "output": {
            "sentence": {
                "begin_time": 0,
                "end_time": 1162,
                "words": [
                    {
                        "text": "床",
                        "begin_time": 0,
                        "end_time": 263,
                        "phonemes": [
                            {
                                "begin_time": 0,
                                "end_time": 119,
                                "text": "ch_c",
                                "tone": 2
                            },
                            {
                                "begin_time": 119,
                                "end_time": 263,
                                "text": "uang_c",
                                "tone": 2
                            }
                        ]
                    },
                    {
                        "text": "前",
                        "begin_time": 263,
                        "end_time": 463,
                        "phonemes": [
                            {
                                "begin_time": 263,
                                "end_time": 375,
                                "text": "q_c",
                                "tone": 2
                            },
                            {
                                "begin_time": 375,
                                "end_time": 463,
                                "text": "ian_c",
                                "tone": 2
                            }
                        ]
                    },
                    {
                        "text": "明",
                        "begin_time": 463,
                        "end_time": 688,
                        "phonemes": [
                            {
                                "begin_time": 463,
                                "end_time": 575,
                                "text": "m_c",
                                "tone": 2
                            },
                            {
                                "begin_time": 575,
                                "end_time": 688,
                                "text": "ing_c",
                                "tone": 2
                            }
                        ]
                    },
                    {
                        "text": "月",
                        "begin_time": 688,
                        "end_time": 863,
                        "phonemes": [
                            {
                                "begin_time": 688,
                                "end_time": 738,
                                "text": "y_c",
                                "tone": 4
                            },
                            {
                                "begin_time": 738,
                                "end_time": 863,
                                "text": "ve_c",
                                "tone": 4
                            }
                        ]
                    },
                    {
                        "text": "光",
                        "begin_time": 863,
                        "end_time": 1150,
                        "phonemes": [
                            {
                                "begin_time": 863,
                                "end_time": 975,
                                "text": "g_c",
                                "tone": 1
                            },
                            {
                                "begin_time": 975,
                                "end_time": 1150,
                                "text": "uang_c",
                                "tone": 1
                            }
                        ]
                    }
                ]
            }
        },
        "usage": null
    }
}

payload参数说明:

参数

类型

说明

output.sentence

begin_time

Integer

句子开始时间,单位为ms。

end_time

Integer

句子结束时间,单位为ms。

words

List[]

包含的词时间戳信息,需要将run-task指令中的word_timestamp_enabled设置为true

sentence.words为字时间戳列表,其中每一个word格式如下:

参数

类型

说明

text

String

文本。

begin_time

Integer

字开始时间,单位为ms。

end_time

Integer

字结束时间,单位为ms。

phonemes

List[]

包含音素时间戳信息,需要将run-task指令中的phoneme_timestamp_enabled设置为true。

words.phonemes为音素时间戳列表,其中每一个phoneme格式如下:

参数

类型

说明

begin_time

Long

音素开始时间

end_time

Long

音素结束时间

text

String

音素

tone

String

音调。英文中0/1/2分别代表轻音/重音/次重音。拼音中1/2/3/4/5分别代表一声/二声/三声/四声/轻声。

5、接收task-finished事件:语音合成任务已结束

服务端在所有语音合成结果返回完毕后返回task-finished事件,标志着合成任务结束。

示例:

{
    "header": {
        "task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx",
        "event": "task-finished",
        "attributes": {}
    },
    "payload": {
        "output": null,
        "usage": {
            "characters": 6
        }
    }
}

任务失败

若接收到task-failed事件,表示任务失败。

task-failed事件:任务失败

客户端收到该事件后结束连接并处理报错。

示例:

{
    "header": {
        "task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx",
        "event": "task-failed",
        "error_code": "CLIENT_ERROR",
        "error_message": "request timeout after 23 seconds.",
        "attributes": {}
    },
    "payload": {}
}

header参数说明:

参数

类型

说明

header.error_code

String

报错类型描述。

header.error_message

String

具体报错原因。

三、关闭连接

接收到task-finished事件后,可以关闭WebSocket连接。通常通过调用工具库中的close函数实现。