实时语音合成(算法编码:2202094)
协议类型:WebSocket(WS)
一、接入点
| 环境 |
地址 |
| 公网 |
wss://api-maas.teleai.com.cn/aipaas/voice/v1/tts/stream |
- API Path :
/aipaas/voice/v1/tts/stream
- 请求方法 :
WebSocket
- Content-Type :
application/json
二、鉴权
采用请求头携带鉴权信息,在建立 WebSocket 连接时通过 Header 传递 :
2.1 标准鉴权
Authorization: <鉴权信息>
| 请求头 |
必填 |
说明 |
Content-Type |
是 |
内容类型,固定 application/json |
X-APP-ID |
是 |
控制台 → 应用管理 → AppID |
Device-Uuid |
是 |
设备uuid |
Authorization |
是 |
鉴权信息 |
2.2 Bearer Token 鉴权
Authorization: Bearer <用户申请的API-KEY>
| 请求头 |
必填 |
说明 |
Content-Type |
是 |
内容类型,固定 application/json |
Device-Uuid |
是 |
设备uuid |
Authorization |
是 |
Bearer |
三、能力简介
英文流式语音合成(教育智能体)模型是一种能够将文本实时转换为语音的技术。这种技术特别适用于需要即时响应的应用场景,如语音助手、在线阅读、导航系统等。
四、请求与响应
4.1 请求参数
| 参数名 |
必填 |
类型 |
说明 |
req_id |
是 |
[string] |
请求全局 ID,记录该值便于排查问题 |
text |
是 |
[string] |
待合成的文本,需要为 UTF-8 编码 |
format |
否 |
[string] |
⾳频采样率,⽀持 16000 Hz 和 8000 Hz,默认值:16000 |
sample_rate |
否 |
[int] |
⾳频采样率,⽀持 16000 Hz 和 8000 Hz,默认值:16000 |
voice |
否 |
[string] |
说话⼈,默认值:yixiaoling, 可支持 yixiaoling、yixiaotian、yixiaoluo、yixiaotong、yixiaohui、yixiaoxuan、yixiaochao、yixiaowei、 yixiaoliang、yixiaoshen、yixiaojing、yixiaoke、yixiaorou、yixiaobei、yixiaoxin-female、yixiaoxin-male、yixiaobin、malili、tianxu、yixiaoqing、yixiaoyao、yixiaoni、yixiaoting、yixiaoqi、yixiaoqiong、yixiaolin、yixiaomeng、yixiaohan、yixiaoze、yixiaohong、yixiaowan、yixiaotang、yixiaoping、yixiaoyan、yixiaolan、yixiaotao、yixiaojun、yixiaolu、yixiaohao、sarah、morgan、molly、clara、 elena、helen。其中sarah、morgan、molly、clara、elena、helen 为纯英文音色,不能说中文 |
volume |
否 |
[int] |
音量,取值范围:[0, 100],默认值:50(英文) |
speech_rate |
否 |
[float] |
语速,取值范围:[0.5, 2.0],默认值:1.0 时,支持方言选择,默认是普通话 |
pitch |
否 |
[float] |
语调,取值范围:[0.5, 2.0],默认值:1.0 |
enable_markdown_filter |
否 |
[boolean] |
是否开启 markdown 过滤,默认值为 false |
4.2 响应体
4.2.1 顶层字段
| 参数名 |
必填 |
类型 |
说明 |
code |
是 |
[int] |
状态码,见服务码说明 |
message |
是 |
[string] |
状态说明,见服务码说明 |
sid |
否 |
[string] |
会话全局唯一 id,用于记录本次会话 |
result |
否 |
[object] |
合成结果 |
4.2.2 合成结果 result
| 参数名 |
必填 |
类型 |
说明 |
result>>audio |
否 |
[string] |
合成音频数据,经过 base64 编码 |
result>>audio_len |
否 |
[int] |
合成音频长度,单位:ms |
result>>phn_seq_len |
否 |
[int] |
合成音频音素序列长度 |
result>>phn_id_seq |
否 |
[array] |
合成音频音素 ID 序列 List |
result>>phn_name_seq |
否 |
[array] |
合成音频音素名称序列 List |
result>>phn_dur_seq |
否 |
[array] |
合成音频音素持续时间序列 List |
result>>is_end |
否 |
[boolean] |
标志位:true 最后一个合成片段;false 中间合成片段 |
五、状态码说明
5.1 算法状态码
通用状态码请参见【状态码】中的【网关认证】,其余状态码如下:
5.1.1 鉴权响应码说明
| 返回码 |
说明 |
错误信息 |
解决方法 |
101 |
成功 |
{"message":"success"} |
成功,开始语音合成 |
4001 |
query 参数错误 |
{"message":"parse queryparameter fail"} |
检查 query 参数格式是否正确 |
4001 |
缺少 app_id 或者 app_id 无效 |
{"message":"loseparameter app_id"} |
检查是否有 app_id 参数或者 app_id 是否有效 |
4001 |
缺少 sign |
{"message":"loseparameter sign"} |
检查是否有 sign 参数 |
4001 |
缺少 timestamp 或者时间戳过期 |
{"message":"invalidtimestamp"} |
检查是否有时间戳参数或者时间戳是否过期,时间戳 10 分钟有效 |
4001 |
签名校验失败 |
{"message":"check signfail"} |
检查签名生成是否错误 |
4002 |
并发请求多 |
{"message":"Too manyrequests"} |
联系商务,增加并发 |
5.1.2 返回码
| 返回码 |
解释 |
说明 |
解决方法 |
10301 |
Required parameter miss |
必填参数缺失 |
检查请求体是否符合接口协议 |
10302 |
Too many requests |
并发请求过多 |
联系商务,增加并发 |
10304 |
Parse request body fail |
请求格式错误 |
查看请求的 URL body 格式是否正确,参考接口文档 |
10503 |
Server connection time out |
服务连接超时 |
联系技术人员 |
10903 |
Synthesis failed |
合成失败 |
联系技术人员 |
10000 |
Success |
成功 |
执行下一步操作 |
5.2 公共状态码
- 接口响应的公共状态码由网关统一定义,一般用于标识鉴权失败、限流或服务异常,具体业务错误码、错误信息及排查建议请参考:开发指南-公共信息及状态码。
六、调用示例
6.1 请求参数示例
{
"req_id": "test001",
"text": "今天是周一,天气挺好的。"
}
6.2 开始合成响应示例
{
"status": 10000,
"status_msg": "Success",
"sid": "4eHgiLCBhbGdvcml"
}
6.3 接收合成数据响应示例
{
"status": 10000,
"status_msg": "Success",
"result": {
"audio": "//6YYmNiDlSUXjZlDP8yADQAQ1E2ANKJOaTqFGUgIwDQAKAA==",
"audio_len": 2500,
"is_end": false,
"phn_seq_len": 10,
"phn_id_seq": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"phn_name_seq": ["j", "in1", "t", "ian1", "z", "en3", "m", "e1", "l", "e5"],
"phn_dur_seq": [150, 350, 150, 350, 150, 350, 150, 350, 150, 350]
}
}
6.4 合成结束响应示例
{
"status": 10000,
"status_msg": "Success",
"result": {
"audio": "//6YYmNiDlSUXjZlDP8yADQAQ1E2ANKJOaTqFGUgIwDQAKAA==",
"audio_len": 2500,
"is_end": true,
"phn_seq_len": 10,
"phn_id_seq": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"phn_name_seq": ["j", "in1", "t", "ian1", "z", "en3", "m", "e1", "l", "e5"],
"phn_dur_seq": [150, 350, 150, 350, 150, 350, 150, 350, 150, 350]
}
}
七、代码调用示例
7.1 Java
import com.alibaba.fastjson.JSONObject;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* 为保证示例可以正常运行,请确认maven中是否存在以下依赖:
*
* org.java-websocket
* Java-WebSocket
* 1.5.3
*
*
* com.alibaba
* fastjson
* 2.0.49
*
*/
public class WebSocketExample extends WebSocketClient {
public WebSocketExample(URI serverUri, Map headers) {
super(serverUri, headers);
}
public static void main(String[] args) {
try {
// 创建WebSocket客户端
URI uri = new URI("算法调用地址");
Map headers = new HashMap();
//调用鉴权
headers.put("Content-Type", "application/json");
headers.put("X-APP-ID", "yourAppId");
headers.put("Authorization", "yourAuthorization");
WebSocketExample client = new WebSocketExample(uri, headers);
client.connect();
// 等待一段时间后关闭连接
Thread.sleep(5000);
client.close();
} catch (URISyntaxException | InterruptedException e) {
e.printStackTrace();
System.out.println("WebSocket error: " + e.getMessage());
}
}
@Override
public void onOpen(ServerHandshake handshakedata) {
System.out.println("WebSocket connection opened");
// 发送消息
try {
// 初始化连接消息
JSONObject initSend = new JSONObject();
initSend.put("req_id", "test001");
initSend.put("text", "今天是周一,天气挺好的。");
send(initSend.toJSONString());
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onMessage(String message) {
System.out.println("Received message: " + message);
}
@Override
public void onClose(int code, String reason, boolean remote) {
System.out.println("WebSocket connection closed: " + reason);
}
@Override
public void onError(Exception ex) {
System.err.println("WebSocket error: " + ex.getMessage());
}
}
7.2 Python
import argparse
import asyncio
import websockets
import json
import base64
import logging
import time
# 配置日志,设置日志级别为INFO,并指定日志格式
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger()
async def uws_call(args):
# 获取命令行参数中的URL
url = args.url
# 记录开始时间
start_time = time.time()
logger.info(f"开始建立websocket连接,获取当前时间:{start_time}")
header = {
# 公网调用鉴权,必传
'X-APP-ID': args.X_APP_ID,
'Authorization': args.Authorization
}
# 建立WebSocket连接
async with websockets.connect(url, extra_headers=header) as websocket:
logger.info("=================================请求参数start_json===============================")
# 构建请求参数
start_json = {
'req_id': 'test001',
'text': '今天是周一,天气挺好的。'
}
logger.info(f"请求参数:{start_json}")
# 发送请求参数
await websocket.send(json.dumps(start_json))
try:
# 接收响应
resp_str = await websocket.recv()
resp_data = json.loads(resp_str)
if resp_data['status'] == 10000:
logger.info('Received initial response: %s', resp_str)
# 记录连接建立(即收到第一个响应)的时间
connection_established_time = time.time()
logger.info('WebSocket connection established in %.2f seconds',
connection_established_time - start_time)
is_end = False
# 打开文件以写入音频数据
with open("test.pcm", "wb") as f:
while not is_end:
# 接收音频数据
resp_audio_str = await websocket.recv()
resp_audio_data = json.loads(resp_audio_str)
if resp_audio_data['status'] != 10000:
logger.error('Received non-success status: %s', resp_audio_data)
break
if 'result' in resp_audio_data:
synth_info = resp_audio_data['result']
logger.info('Received synthesis info: %s', synth_info)
# 解码音频数据并写入文件
audio_wav = base64.b64decode(synth_info['audio'])
f.write(audio_wav)
is_end = synth_info['is_end']
else:
logger.error('No result in response: %s', resp_audio_data)
break
# 计算整个交互的耗时(从尝试连接到接收完所有消息)
total_duration = time.time() - start_time
logger.info('Total interaction duration: %.2f seconds', total_duration)
except websockets.exceptions.ConnectionClosed as e:
logger.error('WebSocket connection closed: %s', e)
# 如果连接关闭了,我们仍然可以记录到目前为止的耗时
logger.info('Duration until connection closed: %.2f seconds', time.time() - start_time)
except Exception as e:
logger.error('An error occurred: %s', e)
# 如果发生错误,我们同样可以记录到目前为止的耗时
logger.info('Duration until error occurred: %.2f seconds', time.time() - start_time)
finally:
# 关闭WebSocket连接
await websocket.close()
def main(args):
# 运行异步函数
asyncio.get_event_loop().run_until_complete(uws_call(args))
if __name__ == '__main__':
# 解析命令行参数
parser = argparse.ArgumentParser()
parser.add_argument('--url', type=str, default='算法调用地址')
parser.add_argument('--X_APP_ID', type=str, default='yourAppId')
parser.add_argument('--Authorization', type=str, default='yourAuthorization')
args = parser.parse_args()
# 调用主函数
main(args)