实时语音翻译:可实现对连续音频流的实时翻译和整句翻译,转换成识别后文本信息并返翻译后文字流。
Hi,您好,欢迎使用有道智云实时语音翻译API接口服务。
如果您想快速体验服务,建议您前往 实时语音翻译体验中心 或者在体验中心右下侧找到小程序二维码,扫描进行体验。
如果您有与我们商务合作的需求,可以通过以下方式联系我们:
商务邮箱: AIcloud_Business@corp.youdao.com
如果您对文档内容有任何疑问,可以通过以下几种方式联系我们:
客服QQ:1906538062
智云翻译技术交流QQ 1群: 652880659
智云翻译技术交流QQ 2群: 669384425
智云翻译技术交流QQ 3群: 807539209
智云翻译技术交流QQ 4群: 936752411
联系邮箱: zhiyun@corp.youdao.com
温馨提示:
本文档主要针对开发人员,接入测试前需要获取应用ID
和应用密钥
;如果您还没有,请按照 新手指南 操作。
平台向每个账户赠送50元的体验金,供用户集成前测试所用,具体资费规则详见 实时语音翻译服务报价 。
接口地址:
wss://openapi.youdao.com/stream_speech_trans?{请求参数}
调用方在集成实时语音翻译API时,请遵循以下规则。
规则 | 描述 |
---|---|
传输方式 | WSS |
字符编码 | 统一使用UTF-8编码 |
响应格式 | JSON |
语音格式 | wav(不压缩、pcm编码) |
语音采样率 | 16k |
语音编码 | 16bit位深的单声道 |
服务接口的调用分为认证、实时通信两阶段。
1 .认证阶段
请求参数格式:
key1=value1&key2=value2&key3=value3&key4=value4
参数说明:
参数 | 类型 | 必填 | 说明 | 示例 |
---|---|---|---|---|
appKey | String | 是 | 已申请的应用ID | 应用ID |
salt | String | 是 | UUID | uuid,唯一通用识别码 |
curtime | String | 是 | 时间戳(秒) | 1522292849 |
sign | String | 是 | 签名 | sha256(应用ID + salt + curtime + 应用密钥) |
signType | String | 是 | 数字签名类型 | v4 |
from | String | 是 | 源语言选择 | 参照下方语言表 |
to | String | 是 | 目标语言选择 | 参照下方语言表 |
format | String | 是 | 音频格式,支持wav | wav |
channel | String | 是 | 声道,支持单声道,值为1 | 1 |
version | String | 是 | api版本 | v1 |
rate | String | 是 | 采样率 | 16000 |
transPattern | String | 否 | 支持的翻译模式: 实时语音识别(stream) 整句翻译(sentence) | sentence |
noitn | String | 否 | 强制把阿拉伯数字转为英文单词 (0/1),默认值为0 表示不强制 | 0 |
签名生成方法如下:
sign=sha256(应用ID
+salt
+curtime
+应用密钥
)。(仅API接入方式的应用有应用密钥)
2. 实时通信阶段
认证成功之后,进入实时通信阶段,此阶段客户端发送音频流和结束标识,并接收转写结果。
2. 2. 1 发送音频流
此阶段客户端通过 binary message 发送音频流,内容为音频的二进制数据,此过程的发送频率将影响文字结果展示的实时性。
建议以 200ms 间隔发送音频数据,若间隔超时 15s 以上,服务端将停止识别。
2. 2. 2 发送结束标识
客户端完成所有音频数据的发送后,需发送一个特殊的 binary message 到服务端作为音频流
发送结束的标识,内容为:
{"end": "true"}
认证结果说明
服务端通过 text message 返回 json字符串的认证结果,参数示例:
参数 | 类型 | 说明 |
---|---|---|
errorCode | String | 错误码,详见 错误代码列表 |
action | String | 状态标识,started:握手,recognition:识别,error:错误 |
result | String | 识别结果数据 |
成功:
{
"result": {},
"action": "started",
"errorCode": "0"
}
失败:
{
"result": "{}",
"action": "error",
"errorCode": "202"
}
识别结果说明
交互过程中,服务端不断通过 text message 返回实时识别结果到客户端,响应结果是以json形式输出(为text message)。
识别结果 result 参数说明:
参数 | 含义 | 说明 |
---|---|---|
bg | 分句开始时间 | 单位毫秒/ms (仅简体中文、通用英文、中英混合包括此字段) |
ed | 分句结束时间 | 单位毫秒/ms (仅简体中文、通用英文、中英混合包括此字段) |
partial | 是否是中间结果 | true:中间结果;false:完整句子 |
segId | 分句 id | 从 1 开始递增 |
context | 识别结果 | |
tranContent | 翻译结果 |
识别结果示例:
{
"result": {
"transPattern": "stream",
"segId": 0,
"bg": 60,
"context": "Have a good day.",
"tranContent": "祝你今天愉快。",
"partial": true,
"ed": 840
},
"errorCode": "0",
"action": "recognition"
}
代码 | 语言 |
---|---|
zh-CHS | 简体中文 |
en | 通用英文 |
enzh | 中英混合 |
ar-IL | 阿拉伯语(以色列) |
ar-JO | 阿拉伯语(约旦) |
ar-AE | 阿拉伯语(阿拉伯联合酋长国) |
ar-BH | 阿拉伯语(巴林) |
ar-DZ | 阿拉伯语(阿尔及利亚) |
ar-SA | 阿拉伯语(沙特阿拉伯) |
ar-IQ | 阿拉伯语(伊拉克) |
ar-KW | 阿拉伯语(科威特) |
ar-MA | 阿拉伯语(摩洛哥) |
ar-TN | 阿拉伯语(突尼斯) |
ar-OM | 阿拉伯语(阿曼) |
ar-PS | 阿拉伯语(巴勒斯坦国) |
ar-QA | 阿拉伯语(卡塔尔) |
ar-LB | 阿拉伯语(黎巴嫩) |
ar-EG | 阿拉伯语(埃及) |
pl | 波兰语(波兰) |
da | 丹麦语(丹麦) |
de | 德语(德国) |
ru | 俄语 |
fr | 法语(法国) |
fr-CA | 法语(加拿大) |
fi | 芬兰语(芬兰) |
ko | 韩语(韩国) |
nl | 荷兰语(荷兰) |
cs | 捷克语(捷克共和国) |
hr | 克罗地亚语(克罗地亚) |
lv | 拉脱维亚语(拉脱维亚) |
ro | 罗马尼亚语(罗马尼亚) |
ms | 马来语(马来西亚) |
pt-BRA | 葡萄牙语(巴西) |
pt | 葡萄牙语(葡萄牙) |
ja | 日语(日本) |
sv | 瑞典语(瑞典) |
te | 泰卢固语(印度) |
ta | 泰米尔语(印度) |
ta-SG | 泰米尔语(新加坡) |
ta-LK | 泰米尔语(斯里兰卡) |
ta-MY | 泰米尔语(马来西亚) |
th | 泰语(泰国) |
tr | 土耳其语(土耳其) |
es | 西班牙语(西班牙) |
es-AR | 西班牙语(阿根廷) |
es-BO | 西班牙语(玻利维亚) |
es-CL | 西班牙语(智利) |
es-COL | 西班牙语(哥伦比亚) |
es-CR | 西班牙语(哥斯达黎加) |
es-EC | 西班牙语(厄瓜多尔) |
es-SV | 西班牙语(萨尔瓦多) |
es-US | 西班牙语(美国) |
es-GT | 西班牙语(危地马拉) |
es-HN | 西班牙语(洪都拉斯) |
es-MEX | 西班牙语(墨西哥) |
es-NI | 西班牙语(尼加拉瓜) |
es-PA | 西班牙语(巴拿马) |
es-PY | 西班牙语(巴拉圭) |
es-PE | 西班牙语(秘鲁) |
es-PR | 西班牙语(波多黎各) |
es-DO | 西班牙语(多米尼加共和国) |
es-UY | 西班牙语(乌拉圭) |
es-VE | 西班牙语(委内瑞拉) |
it | 意大利语(意大利) |
hi | 印地语(印度) |
en-AUS | 英语(澳大利亚) |
en-CA | 英语(加拿大) |
en-GH | 英语(加纳) |
en-GBR | 英语(英国) |
en-IND | 英语(印度) |
en-IE | 英语(爱尔兰) |
en-KE | 英语(肯尼亚) |
en-NZ | 英语(新西兰) |
en-NG | 英语(尼日利亚) |
en-PH | 英语(菲律宾) |
en-SG | 英语(新加坡) |
en-ZAF | 英语(南非) |
en-TZ | 英语(坦桑尼亚) |
en-US | 英语(美国) |
vi | 越南语(越南) |
zh-TWN | 台湾普通话(中国台湾) |
lo | 老挝语(老挝) |
bn | 孟加拉语(孟加拉) |
bn-IN | 孟加拉语(印度) |
tl | 菲律宾语 |
支持格式 | 免费用户最大支持并发 | 单次最大请求时长(s) | 支持语言 |
---|---|---|---|
wav | 10 | 3600 | 参考支持的语言列表 |
错误码 | 含义 |
---|---|
101 | 缺少必填的参数,首先确保必填参数齐全,然后,确认参数书写是否正确。 |
102 | 不支持的语言类型 |
103 | 翻译文本过长 |
104 | 不支持的API类型 |
105 | 不支持的签名类型 |
106 | 不支持的响应类型 |
107 | 不支持的传输加密类型 |
108 | 应用ID无效,注册账号,登录后台创建应用和实例并完成绑定,可获得应用ID和应用密钥等信息 |
109 | batchLog格式不正确 |
110 | 无相关服务的有效实例,应用没有绑定服务实例,可以新建服务实例,绑定服务实例。注:某些服务的翻译结果发音需要tts实例,需要在控制台创建语音合成实例绑定应用后方能使用。 |
111 | 开发者账号无效 |
112 | 请求服务无效 |
113 | q不能为空 |
114 | 不支持的图片传输方式 |
201 | 解密失败,可能为DES,BASE64,URLDecode的错误 |
202 | 签名检验失败 |
203 | 访问IP地址不在可访问IP列表 |
205 | 请求的接口与应用的平台类型不一致,确保接入方式(Android SDK、IOS SDK、API)与创建的应用平台类型一致。如有疑问请参考入门指南 |
206 | 因为时间戳无效导致签名校验失败 |
207 | 重放请求 |
301 | 辞典查询失败 |
302 | 翻译查询失败 |
303 | 服务端的其它异常 |
304 | 会话闲置太久超时 |
401 | 账户已经欠费停 |
402 | offlinesdk不可用 |
411 | 访问频率受限,请稍后访问 |
412 | 长请求过于频繁,请稍后访问 |
1001 | 无效的OCR类型 |
1002 | 不支持的OCR image类型 |
1003 | 不支持的OCR Language类型 |
1004 | 识别图片过大 |
1201 | 图片base64解密失败 |
1301 | OCR段落识别失败 |
1411 | 访问频率受限 |
1412 | 超过最大识别字节数 |
2003 | 不支持的语言识别Language类型 |
2004 | 合成字符过长 |
2005 | 不支持的音频文件类型 |
2006 | 不支持的发音类型 |
2201 | 解密失败 |
2301 | 服务的异常 |
2411 | 访问频率受限,请稍后访问 |
2412 | 超过最大请求字符数 |
3001 | 不支持的语音格式 |
3002 | 不支持的语音采样率 |
3003 | 不支持的语音声道 |
3004 | 不支持的语音上传类型 |
3005 | 不支持的语言类型 |
3006 | 不支持的识别类型 |
3007 | 识别音频文件过大 |
3008 | 识别音频时长过长 |
3009 | 不支持的音频文件类型 |
3010 | 不支持的发音类型 |
3201 | 解密失败 |
3301 | 语音识别失败 |
3302 | 语音翻译失败 |
3303 | 服务的异常 |
3411 | 访问频率受限,请稍后访问 |
3412 | 超过最大请求字符数 |
4001 | 不支持的语音识别格式 |
4002 | 不支持的语音识别采样率 |
4003 | 不支持的语音识别声道 |
4004 | 不支持的语音上传类型 |
4005 | 不支持的语言类型 |
4006 | 识别音频文件过大 |
4007 | 识别音频时长过长 |
4201 | 解密失败 |
4301 | 语音识别失败 |
4303 | 服务的异常 |
4411 | 访问频率受限,请稍后访问 |
4412 | 超过最大请求时长 |
5001 | 无效的OCR类型 |
5002 | 不支持的OCR image类型 |
5003 | 不支持的语言类型 |
5004 | 识别图片过大 |
5005 | 不支持的图片类型 |
5006 | 文件为空 |
5201 | 解密错误,图片base64解密失败 |
5301 | OCR段落识别失败 |
5411 | 访问频率受限 |
5412 | 超过最大识别流量 |
9001 | 不支持的语音格式 |
9002 | 不支持的语音采样率 |
9003 | 不支持的语音声道 |
9004 | 不支持的语音上传类型 |
9005 | 不支持的语音识别 Language类型 |
9301 | ASR识别失败 |
9303 | 服务器内部错误 |
9411 | 访问频率受限(超过最大调用次数) |
9412 | 超过最大处理语音长度 |
10001 | 无效的OCR类型 |
10002 | 不支持的OCR image类型 |
10004 | 识别图片过大 |
10201 | 图片base64解密失败 |
10301 | OCR段落识别失败 |
10411 | 访问频率受限 |
10412 | 超过最大识别流量 |
11001 | 不支持的语音识别格式 |
11002 | 不支持的语音识别采样率 |
11003 | 不支持的语音识别声道 |
11004 | 不支持的语音上传类型 |
11005 | 不支持的语言类型 |
11006 | 识别音频文件过大 |
11007 | 识别音频时长过长,最大支持30s |
11201 | 解密失败 |
11301 | 语音识别失败 |
11303 | 服务的异常 |
11411 | 访问频率受限,请稍后访问 |
11412 | 超过最大请求时长 |
12001 | 图片尺寸过大 |
12002 | 图片base64解密失败 |
12003 | 引擎服务器返回错误 |
12004 | 图片为空 |
12005 | 不支持的识别图片类型 |
12006 | 图片无匹配结果 |
13001 | 不支持的角度类型 |
13002 | 不支持的文件类型 |
13003 | 表格识别图片过大 |
13004 | 文件为空 |
13301 | 表格识别失败 |
15001 | 需要图片 |
15002 | 图片过大(1M) |
15003 | 服务调用失败 |
17001 | 需要图片 |
17002 | 图片过大(1M) |
17003 | 识别类型未找到 |
17004 | 不支持的识别类型 |
17005 | 服务调用失败 |
上线日期 | 版本号 | 更新内容 |
---|---|---|
2019.07.09 | v1.0.0 | 有道智云实时语音翻译API上线 |
2019.08.20 | v1.1.0 | 实时语音翻译API所支持语种扩充至86个 |
package com.youdao.ai;
import javax.websocket.ContainerProvider;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URI;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
public class StreamSpeechDemo {
public Session session;
public static void main(String[] args) throws Exception {
String filePath = "音频文件";
String from = "源语言";
String to = "目标语言";
String appKey = "您的应用ID";
String appSecret = "您的应用密钥";
streamSpeech(appKey,appSecret,filePath, from, to);
}
protected void start(String uri) {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
try {
session = container.connectToServer(Websocket.class, URI.create(uri));
} catch (Exception e) {
e.printStackTrace();
}
}
public void doAsrWebSocketClient(String filePath, String uri, Integer step) {
StreamSpeechDemo streamSpeechDemoWebSocketClientApp = new StreamSpeechDemo();
streamSpeechDemoWebSocketClientApp.start(uri);
try {
InputStream inputStream = new FileInputStream(filePath);
int read = 0;
byte[] bytes = new byte[step];
while ((read = inputStream.read(bytes)) != -1) {
streamSpeechDemoWebSocketClientApp.session.getBasicRemote().sendBinary(ByteBuffer.wrap(bytes));
Thread.sleep(100);
}
byte[] closebytes = "{\"end\": \"true\"}".getBytes();
streamSpeechDemoWebSocketClientApp.session.getBasicRemote().sendBinary(ByteBuffer.wrap(closebytes));
} catch (Exception e) {
e.printStackTrace();
}
}
private static void streamSpeech(String appKey,String appSecret,String filePath,String from, String to) throws NoSuchAlgorithmException {
StreamSpeechDemo asrWebSocketClient = new StreamSpeechDemo();
String nonce = UUID.randomUUID().toString();
String curtime = String.valueOf(System.currentTimeMillis()/1000);
String signStr = appKey + nonce +curtime + appSecret;
String sign = encrypt(signStr,null);
String uri = "wss://openapi.youdao.com/stream_speech_trans?appKey="+appKey+"&salt="+nonce+"&curtime="+curtime+"&sign="+sign+"&version=v1&channel=1&format=wav&signType=v4&rate=16000&from=" + from + "&to=" + to + "&transPattern=stream";
asrWebSocketClient.doAsrWebSocketClient(filePath,uri,6400);
}
/**
* 获取MessageDigest的加密结果
* @param strSrc
* @param encName
* @return
* @throws NoSuchAlgorithmException
*/
public static String encrypt(String strSrc, String encName) throws NoSuchAlgorithmException {
byte[] bt = strSrc.getBytes();
if (encName == null || "".equals(encName)) {
encName = "SHA-256";
}
MessageDigest md = MessageDigest.getInstance(encName);
md.update(bt);
// to HexString
return bytes2Hex(md.digest());
}
public static String bytes2Hex(byte[] bts) {
String des = "";
String tmp;
for (int i = 0; i < bts.length; i++) {
tmp = (Integer.toHexString(bts[i] & 0xFF));
if (tmp.length() == 1) {
des += "0";
}
des += tmp;
}
return des;
}
}
import javax.websocket.*;
import java.io.IOException;
@ClientEndpoint
public class Websocket {
private void print(Object obj) {
System.out.println(obj.toString());
}
Session session;
@OnOpen
public void onOpen(Session session) {
print("Connect to endpoint: " + session.getBasicRemote());
this.session = session;
}
@OnMessage
public void onMessage(String message) throws IOException {
print(message);
if (message.contains("\"errorCode\":\"304\"")) {
onClose();
}
}
@OnError
public void onError(Throwable throwable) {
throwable.printStackTrace();
}
@OnClose
public void onClose() throws IOException {
if(this.session.isOpen()){
this.session.close();
}
print("session close");
System.exit(0);
}
}
# -*- coding: utf-8 -*-
#!/usr/bin/python
import Queue
import argparse
import hashlib
import json
import sys
import threading
import time
import uuid
from ws4py.client.threadedclient import WebSocketClient
#sys.stdout = codecs.getwriter('utf-8')(sys.stdout)
app_key = '您的appKey'
app_secret = '您的应用密钥'
def rate_limited(maxPerSecond):
minInterval = 1.0 / float(maxPerSecond)
def decorate(func):
lastTimeCalled = [0.0]
def rate_limited_function(*args,**kargs):
elapsed = time.clock() - lastTimeCalled[0]
leftToWait = minInterval - elapsed
if leftToWait>0:
time.sleep(leftToWait)
ret = func(*args,**kargs)
lastTimeCalled[0] = time.clock()
return ret
return rate_limited_function
return decorate
class MyClient(WebSocketClient):
def __init__(self, audiofile, url, protocols=None, extensions=None, heartbeat_freq=None, byterate=32000,
save_adaptation_state_filename=None, send_adaptation_state_filename=None):
super(MyClient, self).__init__(url, protocols, extensions, heartbeat_freq)
self.final_hyps = []
self.audiofile = audiofile
self.byterate = byterate
self.final_hyp_queue = Queue.Queue()
self.save_adaptation_state_filename = save_adaptation_state_filename
self.send_adaptation_state_filename = send_adaptation_state_filename
@rate_limited(5)
def send_data(self, data):
self.send(data, binary=True)
print 'data sent',len(data)
def opened(self):
def send_data_to_ws():
if self.send_adaptation_state_filename is not None:
print >> sys.stderr, "Sending adaptation state from %s" % self.send_adaptation_state_filename
try:
adaptation_state_props = json.load(open(self.send_adaptation_state_filename, "r"))
self.send(json.dumps(dict(adaptation_state=adaptation_state_props)))
except:
e = sys.exc_info()[0]
print >> sys.stderr, "Failed to send adaptation state: ", e
with self.audiofile as audiostream:
for block in iter(lambda: audiostream.read(self.byterate/5), ""):
self.send_data(block)
print >> sys.stderr, "Audio sent, now sending end message"
self.send_data('{\"end\": \"true\"}')
t = threading.Thread(target=send_data_to_ws)
t.start()
def received_message(self, m):
print 'received',str(m)
def get_full_hyp(self, timeout=60):
return self.final_hyp_queue.get(timeout)
def closed(self, code, reason=None):
self.final_hyp_queue.put(" ".join(self.final_hyps))
def encrypt(signStr):
hash = hashlib.sha256()
hash.update(signStr.encode('utf-8'))
return hash.hexdigest()
def main():
parser = argparse.ArgumentParser(description='Command line client for kaldigstserver')
parser.add_argument('-u', '--uri', default="wss://openapi.youdao.com/stream_speech_trans", dest="uri", help="Server websocket URI")
parser.add_argument('-r', '--rate', default=32000, dest="rate", type=int, help="Rate in bytes/sec at which audio should be sent to the server. NB! For raw 16-bit audio it must be 2*samplerate!")
parser.add_argument('--from', default="en", dest="lanFrom", help="en or zh-CHS")
parser.add_argument('--to', default="en", dest="lanTo", help="en or zh-CHS")
parser.add_argument('-p', '--pattern', default="sentence", dest="transPattern", help="The pattern of Stream Speech Translation should be sentence or stream")
parser.add_argument('--save-adaptation-state', help="Save adaptation state to file")
parser.add_argument('--send-adaptation-state', help="Send adaptation state from file")
parser.add_argument('--content-type', default='', help="Use the specified content type (empty by default, for raw files the default is audio/x-raw, layout=(string)interleaved, rate=(int), format=(string)S16LE, channels=(int)1")
parser.add_argument('audiofile', help="Audio file to be sent to the server", type=argparse.FileType('rb'), default=sys.stdin)
args = parser.parse_args()
content_type = args.content_type
if content_type == '' and args.audiofile.name.endswith(".raw"):
content_type = "audio/x-raw, layout=(string)interleaved, rate=(int)%d, format=(string)S16LE, channels=(int)1" %(args.rate/2)
nonce = str(uuid.uuid1())
curtime = str(int(time.time()))
signStr = app_key + nonce + curtime + app_secret
print(signStr)
sign = encrypt(signStr)
ws = MyClient(args.audiofile, args.uri + '?appKey=' + app_key + "&salt=" + nonce + "&curtime="
+ curtime + "&sign=" + sign + "&version=v1&channel=1&format=wav&signType=v4&rate=16000&from="
+ args.lanFrom +"&to=" + args.lanTo + "&transPattern=" + args.transPattern, byterate=args.rate,
save_adaptation_state_filename=args.save_adaptation_state, send_adaptation_state_filename=args.send_adaptation_state)
ws.connect()
result = ws.get_full_hyp()
print result.encode('utf-8')
if __name__ == "__main__":
main()
using System;
using System.IO;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Security.Cryptography;
namespace asrliteclient
{
public class AsrDemoWebSocketClient
{
public void start(String filePath, String uri, int step)
{
var client = new ClientWebSocket();
client.ConnectAsync(new Uri(uri), CancellationToken.None).Wait();
startReceiving(client);
int bufferSize = step; //每次读取的字节数
byte[] buffer = new byte[bufferSize];
FileStream stream = null;
try
{
stream = new FileStream(filePath, FileMode.Open);
long fileLength = stream.Length; //文件流的长度
int readCount = (int) Math.Ceiling((double) (fileLength / bufferSize)); //需要对文件读取的次数
int tempCount = 0; //当前已经读取的次数
do
{
stream.Read(buffer, 0,
bufferSize); //分readCount次读取这个文件流,每次从上次读取的结束位置开始读取bufferSize个字节
//这里加入接收和处理数据的逻辑-
var array = new ArraySegment(buffer);
client.SendAsync(array, WebSocketMessageType.Binary, true, CancellationToken.None);
tempCount++;
} while (tempCount < readCount);
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
if (stream != null)
stream.Dispose();
}
try
{
byte[] closeBytes = Encoding.UTF8.GetBytes("{\"end\": \"true\"}");
var closeArray = new ArraySegment(closeBytes);
client.SendAsync(closeArray, WebSocketMessageType.Binary, true, CancellationToken.None);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
while (true)
{
Thread.Sleep(3000);
break;
}
client.Dispose();
}
public async Task startReceiving(ClientWebSocket client)
{
while (true)
{
var array = new byte[4096];
var result = await client.ReceiveAsync(new ArraySegment(array), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Text)
{
string msg = Encoding.UTF8.GetString(array, 0, result.Count);
Console.ForegroundColor = ConsoleColor.DarkBlue;
Console.WriteLine("--> {0}", msg);
Console.ForegroundColor = ConsoleColor.DarkGray;
}
}
}
public void doAscWebSocketClient(String filePath, String uri, int step)
{
start(filePath, uri, step);
}
public void init()
{
String filePath = "D:\\en.wav";
String from = "en";
String to = "zh-CHS";
String appKey = "您的应用ID";
String appSecret = "您的应用密钥";
String nonce = Guid.NewGuid().ToString();
TimeSpan ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc));
long millis = (long) ts.TotalMilliseconds;
String curtime = Convert.ToString(millis / 1000);
String signStr = appKey + nonce + curtime + appSecret;
String sign = encrypt(signStr, null);
String uri = "wss://openapi.youdao.com/stream_speech_trans?appKey=" + appKey + "&salt=" + nonce +
"&curtime=" + curtime + "&sign=" + sign +
"&version=v1&channel=1&format=wav&signType=v4&rate=16000&from=" + from + "&to=" + to + "&transPattern=stream";
Console.WriteLine(uri);
doAscWebSocketClient(filePath, uri, 6400);
}
//加密
private String encrypt(String strSrc, String encName)
{
byte[] bt = Encoding.UTF8.GetBytes(strSrc);
SHA256 sha256 = SHA256.Create();
//暂时固定为SHA—256
//暂时没发现内置的不同hash加密构造方法
//可以自行编写代替
if (encName == null || "".Equals(encName))
{
encName = "SHA-256";
}
byte[] HashData = sha256.ComputeHash(bt);
StringBuilder oSb = new StringBuilder();
for (int x = 0; x < HashData.Length; x++)
{
//hexadecimal string value
oSb.Append(HashData[x].ToString("x2"));
}
return oSb.ToString();
}
public static void Main(string[] args)
{
AsrDemoWebSocketClient asrDemoWebSocketClient = new AsrDemoWebSocketClient();
asrDemoWebSocketClient.init();
}
}
}
<?php
# 需要安装Workerman库或用其他websocket第三方库代替
use Workerman\Worker;
use Workerman\Connection\AsyncTcpConnection;
use Workerman\Protocols\Websocket;
require_once 'C:\Users\谭上鸥\PhpstormProjects\Workerman-master\Autoloader.php';
function start($filePath,$uri,$step){
$worker = new Worker();
$worker->onWorkerStart = function($worker) use($uri,$filePath,$step){
$con = new AsyncTcpConnection($uri);
$con->websocketType = Websocket::BINARY_TYPE_ARRAYBUFFER;
$con->onConnect = function($con) {
};
$con->onMessage = function($con, $data) {
echo $data."\n";
};
$con->connect();
$handle = fopen($filePath, "rb");
// 每次读取一定大小的文件
$readTime = ceil(filesize($filePath)/$step);
$read = 0;
while($read<$readTime){
$content = stream_get_contents($handle,$step,0);
$con->send($content);
$read++;
}
$con->send("{\"end\": \"true\"}");
};
Worker::runAll();
}
//uuid generator
function create_guid(){
$microTime = microtime();
list($a_dec, $a_sec) = explode(" ", $microTime);
$dec_hex = dechex($a_dec* 1000000);
$sec_hex = dechex($a_sec);
ensure_length($dec_hex, 5);
ensure_length($sec_hex, 6);
$guid = "";
$guid .= $dec_hex;
$guid .= create_guid_section(3);
$guid .= '-';
$guid .= create_guid_section(4);
$guid .= '-';
$guid .= create_guid_section(4);
$guid .= '-';
$guid .= create_guid_section(4);
$guid .= '-';
$guid .= $sec_hex;
$guid .= create_guid_section(6);
return $guid;
}
function ensure_length(&$string, $length){
$strlen = strlen($string);
if($strlen < $length)
{
$string = str_pad($string,$length,"0");
}
else if($strlen > $length)
{
$string = substr($string, 0, $length);
}
}
function create_guid_section($characters){
$return = "";
for($i=0; $i<$characters; $i++)
{
$return .= dechex(mt_rand(0,15));
}
return $return;
}
function init(){
$filePath = "D:\\en.wav";
$from = "en";
$to = "zh-CHS";
$appKey = "您的应用ID";
$appSecret = "您的应用密钥";
$nonce = create_guid();
$curtime = strtotime("now");
$signStr = $appKey.$nonce.$curtime.$appSecret;
$sign = hash("sha256",$signStr);
$uri = "wss://openapi.youdao.com/stream_asropenapi?appKey=".$appKey."&salt=".$nonce."&curtime=".$curtime."&sign=".$sign."&version=v1&channel=1&format=wav&signType=v4&rate=16000&transPattern=stream&from=".$from."&to=".$to;
echo $uri;
start($filePath,$uri,1600);
}
init();
?>