JAVA对接腾讯语音实时识别引擎

一、官网地址

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,用来区分回调通道。

你可能感兴趣的:(@JAVA学习,ASR,语音识别)