一、官网地址
https://cloud.tencent.com/document/product/1093/35727
需要对接的朋友们,需要咨仔细的看一下文档,主要是一些重要参数,但是小编觉得,腾讯的这个SDK 真的不太友好,demo给的也不是很直接,需要我们自己再次封装,并且SDK不能从中央 仓库直接获取,需要我们自己下载源码,自己搞。。。。
二、对接流程
2.1 先搞jar
我们需要从官网地址下载SDK源码,然后将源码导入我们的IDE中,将out文件夹中的real_asr_sdk_1.6.jar 核心jar包导入到我们自己项目中,如果你的项目是maven方式的话,可以参考小编的文档《maven手动将本地jar包加入到本地maven仓库》。然后还需要引入一下辅助jar,也就是 源码中lib文件夹下面的jar,如果项目中已经有对应的jar,可以直接使用,这些jar可以在中央仓库直接下载。
2.2 代码
我才用的是异步回传结果的方式,因为我的上游是一个ws接口,这个接口不断的接受到语音流,然后我调用腾讯的识别接口,将需要识别的语音流添加到任务中,然后在回调函数中获得识别结果。
任务类:
package com.jack.chat.asrtencent.service;
import com.tencent.cloud.asr.realtime.sdk.asyn_sender.ReceiverEntrance;
import com.jack.chat.fs.service.FsService;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import javax.sound.midi.Soundbank;
public class VoiceTask {
private ReceiverEntrance receiverEntrance;
private String taskId;
public VoiceTask(String taskId) {
this.taskId = taskId;
}
/**
* @Description:
* @author: zhenghao
* @date: 2020/8/13 19:00
*/
public void init(FsService fsService,String tel) {
System.out.println("初始化成功");
// 新建一个服务
this.receiverEntrance = new ReceiverEntrance(Integer.parseInt(taskId));
// 启动服务
this.receiverEntrance.start();
// 注册N个回调Handler
this.receiverEntrance.registerReponseHandler(new MyResponseHandler(this.taskId,tel,fsService));
System.out.println("初始化完成");
}
/**
* 创建和启动服务线程。包括:数据添加线程、发送线程、通知线程。
*/
public void start(byte[] contentStream) {
receiverEntrance.add(contentStream);
// 开始添加数据
// this.voiceAddingTask = new VoiceAddingTask(this.receiverEntrance,contentStream );
// this.voiceAddingTask.start();
// 10秒后停止任务/关闭服务。如需一直使用,则不要调用它。
/*this.sleepSomeTime(10000);
this.stop();*/
}
public void stop() {
this.receiverEntrance.stopService();
}
}
核心类:每一个通道我们都需要new 一个核心类
package com.jack.chat.asrtencent.service;
import com.tencent.cloud.asr.realtime.sdk.config.AsrBaseConfig;
import com.tencent.cloud.asr.realtime.sdk.config.AsrGlobelConfig;
import com.tencent.cloud.asr.realtime.sdk.config.AsrInternalConfig;
import com.tencent.cloud.asr.realtime.sdk.config.AsrPersonalConfig;
import com.tencent.cloud.asr.realtime.sdk.model.enums.ResponseEncode;
import com.tencent.cloud.asr.realtime.sdk.model.enums.SdkRole;
import com.tencent.cloud.asr.realtime.sdk.model.enums.VoiceFormat;
import com.tencent.cloud.asr.realtime.sdk.utils.ByteUtils;
import com.jack.chat.fs.service.FsService;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
public class RasrAsynRequestSample {
private VoiceTask voiceTask;
private FsService fsService;
static {
initBaseParameters();
}
public RasrAsynRequestSample(String taskId, VoiceTask voiceTask) {
this.taskId = taskId;
this.voiceTask = voiceTask;
}
public void init(String tel) {
System.out.println("开始反射fsservice");
WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
this.fsService = (FsService) wac.getBean("fsService");
System.out.println("反射完成");
this.voiceTask.init(this.fsService,tel);
}
private int threadNumber = 1;
private String taskId = "0";
// private String voiceFile = "test_wav/8k/8k.wav";
private List taskList = new ArrayList();
public static void main(String[] args) throws Exception {
RasrAsynRequestSample rasrRequestSample = new RasrAsynRequestSample("1", new VoiceTask("1"));
rasrRequestSample.init("18333612608");
rasrRequestSample.setArguments(args);
String audio_data = "C:\\data\\8k.wav";
int subLength = 320;
// byte[] stream = new byte[320];
// BufferedInputStream bufferedInputSteam = new BufferedInputStream(new FileInputStream(new File(audio_data)));
// int len;
// while ((len = bufferedInputSteam.read(stream)) > 0) {
// rasrRequestSample.start(stream);
// Thread.sleep(125);
// }
List list = ByteUtils.subToSmallBytes(new File(audio_data), subLength);
for (int i = 0; i < list.size(); i++) {
rasrRequestSample.start(list.get(i));
// sleepSomeTime(125); // 对于8K的语音,每秒发出2048*8 = 16KB数据,与实际相符。
}
// rasrRequestSample.start();
sleepSomeTime(600000); // 10分钟后停止示例程序。
rasrRequestSample.stop();
System.exit(0);
}
/**
* 根据需求启动多个任务,每个任务都独立运行,互不干扰。
*/
public void start(byte[] contentSteam) {
for (int i = 1; i <= this.threadNumber; i++) {
this.taskList.add(voiceTask);
voiceTask.start(contentSteam);
sleepSomeTime(20);
}
}
/**
* 停止全部任务。
*/
public void stop() {
voiceTask.stop();
}
/**
* 初始化基础参数, 请将下面的参数值配置成你自己的值。
*
* 参数获取方法可参考: 签名鉴权 获取签名所需信息
*/
private static void initBaseParameters() {
// Required
AsrBaseConfig.appId = "";
AsrBaseConfig.secretKey = "";
AsrBaseConfig.secretId = "";
// optional,根据自身需求配置值
AsrInternalConfig.setSdkRole(SdkRole.VAD); // VAD版用户请务必赋值为 SdkRole.VAD
AsrPersonalConfig.responseEncode = ResponseEncode.UTF_8;
AsrPersonalConfig.engineModelType = "8k_0";
AsrPersonalConfig.voiceFormat = VoiceFormat.wav;
// optional, 可忽略
AsrGlobelConfig.CUT_LENGTH = 4096; // 每次发往服务端的语音分片的字节长度,8K语音建议设为4096,16K语音建议设为8192。
// AsrGlobelConfig.NEED_VAD = 0; // 是否要做VAD,默认为1,表示要做。线上用户不适用,请忽略。
AsrGlobelConfig.NOTIFY_ALL_RESPONSE = true; // 是否回调每个分片的回复。如只需最后的结果,可设为false。
// AsrBaseConfig.PRINT_CUT_REQUEST = true; // 打印每个分片的请求,用于测试。
AsrBaseConfig.PRINT_CUT_RESPONSE = true; // 打印中间结果,用于测试,生产环境建议设为false。
// 默认使用自定义连接池,连接数可在AsrGlobelConfig中修改,更多细节参数,可直接修改源码HttpPoolingManager.java,然后自行打Jar包。
// AsrGlobelConfig.USE_CUSTOM_CONNECTION_POOL = true;
}
private static void sleepSomeTime(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
// ignore
}
}
/**
* 生成可执行Jar后,运行Jar时传入参数的简单处理。不建议这样子传参数。
*
* 比如这个命令传入了4个参数: java -jar realAsrSdk_run.jar 10 test_wav/8k/8k_19s.wav 8k false
*
* 详情可见:out_runnable_jar/command_reference.txt
*/
private void setArguments(String[] args) {
// if (args.length > 0)
// this.threadNumber = Integer.parseInt(args[0]); // 使用传入的参数赋值线程个数
// if (args.length > 1) {
this.voiceFile = args[1];
this.checkSetVoiceFormat(this.voiceFile);
// }
// if (args.length > 2) {
// AsrPersonalConfig.engineModelType = args[2];
// if (AsrPersonalConfig.engineModelType == "16k_0")
// AsrGlobelConfig.CUT_LENGTH = 8192; // 16K语音 也设置成每秒发4次请求,优化演示效果。
// }
// if (args.length > 3){
// AsrGlobelConfig.NOTIFY_ALL_RESPONSE = Boolean.parseBoolean(args[3]);
// }
//
}
private void checkSetVoiceFormat(String voiceFile) {
int index = voiceFile.lastIndexOf(".");
if (index == -1){
return;
}
String formatName = voiceFile.substring(index + 1).trim().toLowerCase();
AsrPersonalConfig.voiceFormat = VoiceFormat.parse(formatName);
}
}
回调类:
package com.jack.chat.asrtencent.service;// --------------------------------------------------------------------------------------------------------------
import com.tencent.cloud.asr.realtime.sdk.cache_handler.FlowHandler;
import com.tencent.cloud.asr.realtime.sdk.model.response.TimeStat;
import com.tencent.cloud.asr.realtime.sdk.model.response.VadResponse;
import com.tencent.cloud.asr.realtime.sdk.model.response.VadResult;
import com.tencent.cloud.asr.realtime.sdk.model.response.VoiceResponse;
import com.tencent.cloud.asr.realtime.sdk.utils.JacksonUtil;
import com.zqf.chat.asr.model.AsrResultModel;
import com.zqf.chat.fs.service.FsService;
import com.zqf.chat.socket.service.WebSocketMapUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 用户自己写的回调Handler.被NotifyService服务线程(可理解为用户线程B)调用。
*/
public class MyResponseHandler implements FlowHandler {
private String handlerId;
private FsService fsService;
private String tel;
public AtomicInteger okLineCount = new AtomicInteger(1);
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
public MyResponseHandler(String handlerId, String tel, FsService fsService) {
this.handlerId = handlerId;
this.tel = tel;
this.fsService = fsService;
}
/**
* 回复数据通过此方法通知到用户。
*/
@Override
public void onUpdate(Object... params) {
VadResponse response = (VadResponse) params[0]; // Vad版用户请用此行代替下面一行
// VoiceResponse response = (VoiceResponse) params[0];
// Your own logic.
System.out.println(sdf.format(new Date()) + " Handler_" + this.handlerId + ", received response -->: "
+ response.getOriginalText());
System.out.println("所有:" + JacksonUtil.toJsonString(response));
// 可以查看延迟。
// this.printDelay(response);
// 可以提取出当前Vad断句回复。适用于Vad版用户,线上用户请忽略。
if (response.getResultList() != null) {
for (VadResult vadResult : response.getResultList()) {
int lineNo = okLineCount.get();
String content = vadResult.getVoiceTextStr();
//判断是否是整句结束
if (2 == vadResult.getSliceType()) {
okLineCount.addAndGet(1);
}
System.out.println("Received vad response: " + vadResult.getVoiceTextStr());
AsrResultModel asrResultModel = new AsrResultModel();
asrResultModel.setLineNo(lineNo);
asrResultModel.setResult(content);
try {
if (StringUtils.isNotEmpty(content)) {
String telChannel = fsService.getTelChannel(tel);
String message = fsService.message(tel, asrResultModel);
System.out.println("telChannel:" + telChannel + "message" + message);
WebSocketMapUtil.getUserWs(telChannel).sendMessage(message);
}
} catch (Exception e) {
System.out.println("tencent发送消息失败:" + e.getMessage());
}
}
}
}
/**
* 查看和打印延迟信息。延迟统计方法可从项目docs目录中查看,或浏览下面的含义解释。
*
*
* 实例如下:
* <<>> write delay: 103 ms, node delay: 103 ms, notify delay: 105 ms. Pre average write: 101 ms, node: 102 ms.
* 分别表示:发送延迟、节点延迟、通知延迟。前面N个分片的平均发送、平均节点延迟。
*
* 如需测试语音发完后多久能收完全部识别结果,可从:“<<>>” 中的notify delay获得参考。建议以write Delay作为服务性能考量。
*
* 延迟含义解释:
* WriteDelay: 数据收发和网络延迟+解析Delay(1-2ms)。
* NodeDelay: 数据滞留延迟 + WriteDelay。 即:从客户add完成1个分片数据开始,至分片结果收到为止,期间总的时间消耗。
* NotifyDelay: NodeDelay + 客户onHander Delay(处理回复耗时)。此值若大于NodeDelay且在增长,会导致Response堆积,最终内存溢出。
*
*/
private void printDelay(VoiceResponse voiceResponse) {
TimeStat timeStat = voiceResponse.getTimeStat();
if (voiceResponse.isEndCut()) {
this.printDelay("<<>>", timeStat);
} else {
this.printDelay("<<>>", timeStat);
}
}
private void printDelay(String cutType, TimeStat timeStat) {
System.out.println(sdf.format(new Date()) + " " + cutType + " write delay: " + timeStat.getWriteDelay()
+ " ms, node delay: " + timeStat.getNodeDelay() + " ms, notify delay: " + timeStat.getNotifyDelay()
+ " ms. Pre average write: " + timeStat.getPreAverageWriteDelay() + " ms, node: "
+ timeStat.getPreAverageNodeDelay() + " ms.");
}
}
调用类:
System.out.println("使用腾讯");
String s = RandomUtils.generateNumber(9);
rasrAsynRequestSample = new RasrAsynRequestSample(s, new VoiceTask(s));
rasrAsynRequestSample.init(tel);
在建立ws接口成功以后执行上面调用代码,从传输的参数可以知道,我是根据不同的手机号码进行区分回调通道的, 每个回调类中都有一个私有变量tel,用来区分回调通道。