freeswitch对接FunASR实时语音听写

1、镜像启动

通过下述命令拉取并启动FunASR软件包的docker镜像:

sudo docker pull \registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.7
mkdir -p ./funasr-runtime-resources/models
sudo docker run -p 10096:10095 -it --privileged=true \-v $PWD/funasr-runtime-resources/models:/workspace/models \registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.7

服务端启动

docker启动之后,启动 funasr-wss-server-2pass服务程序:

cd FunASR/runtime
nohup bash run_server_2pass.sh \--download-model-dir /workspace/models \--vad-dir damo/speech_fsmn_vad_zh-cn-16k-common-onnx \--model-dir damo/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-onnx  \--online-model-dir damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online-onnx  \--punc-dir damo/punc_ct-transformer_zh-cn-common-vad_realtime-vocab272727-onnx \--lm-dir damo/speech_ngram_lm_zh-cn-ai-wesp-fst \--itn-dir thuduj12/fst_itn_zh --certfile 0 \--hotword /workspace/models/hotwords.txt > log.txt 2>&1 &# 如果您想关闭ssl,增加参数:--certfile 0 这里不需要ssl 需要加参数
# 如果您想使用时间戳或者nn热词模型进行部署,请设置--model-dir为对应模型:
#   damo/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-onnx(时间戳)
#   damo/speech_paraformer-large-contextual_asr_nat-zh-cn-16k-common-vocab8404-onnx(nn热词)
# 如果您想在服务端加载热词,请在宿主机文件./funasr-runtime-resources/models/hotwords.txt配置热词(docker映射地址为/workspace/models/hotwords.txt):
#   每行一个热词,格式(热词 权重):阿里巴巴 20(注:热词理论上无限制,但为了兼顾性能和效果,建议热词长度不超过10,个数不超过1k,权重1~100)

 运行命令之后就会下载社区的模型  启动成功之后会有端口显示

接下来就可以做语音翻译了

2.获取freeswitch音频发送到Funasr 可以参考之前获取的

freeswitch智能外呼系统搭建流程_freeswitch 外联模式的流程-CSDN博客

3.java 用ws 连接就行了 官方的样例

//
// Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights
// Reserved. MIT License  (https://opensource.org/licenses/MIT)
//
/*
 * // 2022-2023 by zhaomingwork@qq.com
 */
// java FunasrWsClient
// usage:  FunasrWsClient [-h] [--port PORT] [--host HOST] [--audio_in AUDIO_IN] [--num_threads NUM_THREADS]
//                 [--chunk_size CHUNK_SIZE] [--chunk_interval CHUNK_INTERVAL] [--mode MODE]
package websocket;

import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.*;
import java.util.Map;
import net.sourceforge.argparse4j.ArgumentParsers;
import net.sourceforge.argparse4j.inf.ArgumentParser;
import net.sourceforge.argparse4j.inf.ArgumentParserException;
import net.sourceforge.argparse4j.inf.Namespace;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft;
import org.java_websocket.handshake.ServerHandshake;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** This example demonstrates how to connect to websocket server. */
public class FunasrWsClient extends WebSocketClient {

  public class RecWavThread extends Thread {
    private FunasrWsClient funasrClient;

    public RecWavThread(FunasrWsClient funasrClient) {
      this.funasrClient = funasrClient;
    }

    public void run() {
      this.funasrClient.recWav();
    }
  }

  private static final Logger logger = LoggerFactory.getLogger(FunasrWsClient.class);

  public FunasrWsClient(URI serverUri, Draft draft) {
    super(serverUri, draft);
  }

  public FunasrWsClient(URI serverURI) {
    
    super(serverURI);
  }

  public FunasrWsClient(URI serverUri, Map<String, String> httpHeaders) {
    super(serverUri, httpHeaders);
  }

  public void getSslContext(String keyfile, String certfile) {
    // TODO
    return;
  }

  // send json at first time
  public void sendJson(
      String mode, String strChunkSize, int chunkInterval, String wavName, boolean isSpeaking,String suffix) {
    try {

      JSONObject obj = new JSONObject();
      obj.put("mode", mode);
      JSONArray array = new JSONArray();
      String[] chunkList = strChunkSize.split(",");
      for (int i = 0; i < chunkList.length; i++) {
        array.add(Integer.valueOf(chunkList[i].trim()));
      }

      obj.put("chunk_size", array);
      obj.put("chunk_interval", new Integer(chunkInterval));
      obj.put("wav_name", wavName);

      if(FunasrWsClient.hotwords.trim().length()>0)
      {
          String regex = "\\d+";
          JSONObject jsonitems = new JSONObject();
          String[] items=FunasrWsClient.hotwords.trim().split(" ");
          Pattern pattern = Pattern.compile(regex);
          String tmpWords="";
          for(int i=0;i<items.length;i++)
          {

              Matcher matcher = pattern.matcher(items[i]);

              if (matcher.matches()) {

                jsonitems.put(tmpWords.trim(), items[i].trim());
                tmpWords="";
                continue;
              }
              tmpWords=tmpWords+items[i]+" ";

          }

          obj.put("hotwords", jsonitems.toString());
      }

      if(suffix.equals("wav")){
          suffix="pcm";
      }
      obj.put("wav_format", suffix);
      if (isSpeaking) {
        obj.put("is_speaking", new Boolean(true));
      } else {
        obj.put("is_speaking", new Boolean(false));
      }
      logger.info("sendJson: " + obj);
      // return;

      send(obj.toString());

      return;
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  // send json at end of wav
  public void sendEof() {
    try {
      JSONObject obj = new JSONObject();

      obj.put("is_speaking", new Boolean(false));

      logger.info("sendEof: " + obj);
      // return;

      send(obj.toString());
      iseof = true;
      return;
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  // function for rec wav file
  public void recWav() {
    String fileName=FunasrWsClient.wavPath;
    String suffix=fileName.split("\\.")[fileName.split("\\.").length-1];
    sendJson(mode, strChunkSize, chunkInterval, wavName, true,suffix);
    File file = new File(FunasrWsClient.wavPath);

    int chunkSize = sendChunkSize;
    byte[] bytes = new byte[chunkSize];

    int readSize = 0;
    try (FileInputStream fis = new FileInputStream(file)) {
      if (FunasrWsClient.wavPath.endsWith(".wav")) {
        fis.read(bytes, 0, 44); //skip first 44 wav header
      }
      readSize = fis.read(bytes, 0, chunkSize);
      while (readSize > 0) {
        // send when it is chunk size
        if (readSize == chunkSize) {
          send(bytes); // send buf to server

        } else {
          // send when at last or not is chunk size
          byte[] tmpBytes = new byte[readSize];
          for (int i = 0; i < readSize; i++) {
            tmpBytes[i] = bytes[i];
          }
          send(tmpBytes);
        }
        // if not in offline mode, we simulate online stream by sleep
        if (!mode.equals("offline")) {
          Thread.sleep(Integer.valueOf(chunkSize / 32));
        }

        readSize = fis.read(bytes, 0, chunkSize);
      }

      if (!mode.equals("offline")) {
        // if not offline, we send eof and wait for 3 seconds to close
        Thread.sleep(2000);
        sendEof();
        Thread.sleep(3000);
        close();
      } else {
        // if offline, just send eof
        sendEof();
      }

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  @Override
  public void onOpen(ServerHandshake handshakedata) {

    RecWavThread thread = new RecWavThread(this);
    thread.start();
  }

  @Override
  public void onMessage(String message) {
    JSONObject jsonObject = new JSONObject();
    JSONParser jsonParser = new JSONParser();
    logger.info("received: " + message);
    try {
      jsonObject = (JSONObject) jsonParser.parse(message);
      logger.info("text: " + jsonObject.get("text"));
      if(jsonObject.containsKey("timestamp"))
      {
          logger.info("timestamp: " + jsonObject.get("timestamp"));
      }
    } catch (org.json.simple.parser.ParseException e) {
      e.printStackTrace();
    }
    if (iseof && mode.equals("offline") && !jsonObject.containsKey("is_final")) {
      close();
    }
     
    if (iseof && mode.equals("offline") && jsonObject.containsKey("is_final") && jsonObject.get("is_final").equals("false")) {
      close();
    }
  }

  @Override
  public void onClose(int code, String reason, boolean remote) {

    logger.info(
        "Connection closed by "
            + (remote ? "remote peer" : "us")
            + " Code: "
            + code
            + " Reason: "
            + reason);
  }

  @Override
  public void onError(Exception ex) {
    logger.info("ex: " + ex);
    ex.printStackTrace();
    // if the error is fatal then onClose will be called additionally
  }

  private boolean iseof = false;
  public static String wavPath;
  static String mode = "online";
  static String strChunkSize = "5,10,5";
  static int chunkInterval = 10;
  static int sendChunkSize = 1920;
  static String hotwords="";
  static String fsthotwords="";

  String wavName = "javatest";

  public static void main(String[] args) throws URISyntaxException {
    ArgumentParser parser = ArgumentParsers.newArgumentParser("ws client").defaultHelp(true);
    parser
        .addArgument("--port")
        .help("Port on which to listen.")
        .setDefault("8889")
        .type(String.class)
        .required(false);
    parser
        .addArgument("--host")
        .help("the IP address of server.")
        .setDefault("127.0.0.1")
        .type(String.class)
        .required(false);
    parser
        .addArgument("--audio_in")
        .help("wav path for decoding.")
        .setDefault("asr_example.wav")
        .type(String.class)
        .required(false);
    parser
        .addArgument("--num_threads")
        .help("num of threads for test.")
        .setDefault(1)
        .type(Integer.class)
        .required(false);
    parser
        .addArgument("--chunk_size")
        .help("chunk size for asr.")
        .setDefault("5, 10, 5")
        .type(String.class)
        .required(false);
    parser
        .addArgument("--chunk_interval")
        .help("chunk for asr.")
        .setDefault(10)
        .type(Integer.class)
        .required(false);

    parser
        .addArgument("--mode")
        .help("mode for asr.")
        .setDefault("offline")
        .type(String.class)
        .required(false);
    parser
        .addArgument("--hotwords")
        .help("hotwords, splited by space, hello 30 nihao 40")
        .setDefault("")
        .type(String.class)
        .required(false);
    String srvIp = "";
    String srvPort = "";
    String wavPath = "";
    int numThreads = 1;
    String chunk_size = "";
    int chunk_interval = 10;
    String strmode = "offline";
    String hot="";
    try {
      Namespace ns = parser.parseArgs(args);
      srvIp = ns.get("host");
      srvPort = ns.get("port");
      wavPath = ns.get("audio_in");
      numThreads = ns.get("num_threads");
      chunk_size = ns.get("chunk_size");
      chunk_interval = ns.get("chunk_interval");
      strmode = ns.get("mode");
      hot=ns.get("hotwords");
      System.out.println(srvPort);

    } catch (ArgumentParserException ex) {
      ex.getParser().handleError(ex);
      return;
    }

    FunasrWsClient.strChunkSize = chunk_size;
    FunasrWsClient.chunkInterval = chunk_interval;
    FunasrWsClient.wavPath = wavPath;
    FunasrWsClient.mode = strmode;
    FunasrWsClient.hotwords=hot;
    System.out.println(
        "serIp="
            + srvIp
            + ",srvPort="
            + srvPort
            + ",wavPath="
            + wavPath
            + ",strChunkSize"
            + strChunkSize);

    class ClientThread implements Runnable {

      String srvIp;
      String srvPort;

      ClientThread(String srvIp, String srvPort, String wavPath) {
        this.srvIp = srvIp;
        this.srvPort = srvPort;
      }

      public void run() {
        try {

          int RATE = 16000;
          String[] chunkList = strChunkSize.split(",");
          int int_chunk_size = 60 * Integer.valueOf(chunkList[1].trim()) / chunkInterval;
          int CHUNK = Integer.valueOf(RATE / 1000 * int_chunk_size);
          int stride =
              Integer.valueOf(
                  60 * Integer.valueOf(chunkList[1].trim()) / chunkInterval / 1000 * 16000 * 2);
          System.out.println("chunk_size:" + String.valueOf(int_chunk_size));
          System.out.println("CHUNK:" + CHUNK);
          System.out.println("stride:" + String.valueOf(stride));
          FunasrWsClient.sendChunkSize = CHUNK * 2;

          String wsAddress = "ws://" + srvIp + ":" + srvPort;

          FunasrWsClient c = new FunasrWsClient(new URI(wsAddress));

          c.connect();

          System.out.println("wsAddress:" + wsAddress);
        } catch (Exception e) {
          e.printStackTrace();
          System.out.println("e:" + e);
        }
      }
    }
    for (int i = 0; i < numThreads; i++) {
      System.out.println("Thread1 is running...");
      Thread t = new Thread(new ClientThread(srvIp, srvPort, wavPath));
      t.start();
    }
  }
}
 

运行之后java和Funasr控制台 就能识别了

服务器性能配置官方参考。

用户可以根据自己的业务需求,选择合适的服务器配置,推荐配置为:

  • 配置1: (X86,计算型),4核vCPU,内存8G,单机可以支持大约16路的请求
  • 配置2: (X86,计算型),16核vCPU,内存32G,单机可以支持大约32路的请求
  • 配置3: (X86,计算型),64核vCPU,内存128G,单机可以支持大约100路的请求

详细性能测试报告https://github.com/alibaba-damo-academy/FunASR/blob/main/runtime/docs/benchmark_onnx_cpp.md

官方文档

https://github.com/alibaba-damo-academy/FunASR/blob/main/runtime/docs/SDK_advanced_guide_online_zh.md

如果模块或流程觉得麻烦可以到 

https://item.taobao.com/item.htm?id=653611115230

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/450929.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ctfshow web-77

开启环境: 先直接用伪协议获取 flag 位置。 c?><?php $anew DirectoryIterator("glob:///*"); foreach($a as $f) {echo($f->__toString(). );} exit(0); ?> 发现 flag36x.txt 文件。同时根目录下还有 readflag&#xff0c;估计需要调用 readflag 获…

java hutool工具类实现将数据下载到excel

通过hutool工具类&#xff0c;对于excel的操作变得非常简单&#xff0c;上篇介绍的是excel的上传&#xff0c;对excel的操作&#xff0c;核心代码只有一行。本篇的excel的下载&#xff0c;核心数据也不超过两行&#xff0c;简洁方便&#xff0c;特别适合当下的低代码操作。 下载…

Kettle 解决数据同步缓慢及性能效率问题 (数据同步利用时间戳解耦,性能通过配置优化提升90%)

一. 介绍 在数据同步过程中&#xff0c;缓慢的同步速度和低效率的性能往往是令人头痛的问题。本文将介绍如何通过Kettle解决数据同步缓慢及性能效率问题&#xff0c;其中主要涉及数据同步利用时间戳解耦和通过配置优化提升性能高达90%的方法 。 在先前的博客文章中&#xff0c…

LabVIEW风力发电机在线监测

LabVIEW风力发电机在线监测 随着可再生能源的发展&#xff0c;风力发电成为越来越重要的能源形式。设计了一个基于控制器局域网&#xff08;CAN&#xff09;总线和LabVIEW的风力发电机在线监测系统&#xff0c;实现风力发电机的实时监控和故障诊断&#xff0c;以提高风力发电的…

Vue.js设计与实现(霍春阳)

Vue.js设计与实现 (霍春阳) 电子版获取链接&#xff1a;Vue.js设计与实现(霍春阳) 编辑推荐 适读人群 &#xff1a;1.对Vue.js 2/3具有上手经验&#xff0c;且希望进一步理解Vue.js框架设计原理的开发人员&#xff1b; 2.没有使用过Vue.js&#xff0c;但对Vue.js框架设计感兴趣…

个性化DIY制作硬模空心耳机壳使用什么?

制作硬模空心耳机壳需要使用到以下工具和材料&#xff1a; 硬质材料&#xff1a;如金属、塑料、UV树脂胶液等&#xff0c;用于制作耳机壳的硬质部分。模具&#xff1a;用于制作耳机壳的形状和尺寸&#xff0c;可以使用塑料、金属等材料制作模具。连接器和线材&#xff1a;用于…

AES加密原理

AES是一个迭代的、分组密码加密方式&#xff0c;可以使用128 、192和256位密钥。与 公共密钥密码使用密钥对不同&#xff0c;对称密钥密码使用相同的密钥加密和解密数据。 通过分组密码返回的加密数据的位数与输入数据相同。迭代加密使用一个循环结 构&#xff0c;在该循环中重…

vue3项目中如何实现图片的二次处理

在说这个之前&#xff0c;我们要先理解图片的二次处理与图片压缩之间的差异。 图片的二次处理除了对图片进行压缩&#xff0c;还可以进行更多特效的处理&#xff0c;可以对图片进行宽高的重设&#xff0c;图像位置适应性调整&#xff0c;清晰的的控制&#xff0c;背景颜色的添…

90.网游逆向分析与插件开发-游戏窗口化助手-项目需求与需求拆解

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;实现物品使用策略的功能-CSDN博客 项目需求&#xff1a; 在游戏窗口化时&#xff0c;可以在游戏之外弹出一个窗口&#xff0c;可以隐藏或者显示游戏窗口&#xff0c;显示游戏人物的基本状态&#xff…

CMake生成osg的FFMPEG插件及Windows下不生成VS工程问题解决

在Windows下&#xff0c;如何利用CMake生成osg的FFMPEG插件&#xff0c;请参考如下博文&#xff0c;同生成jpeg插件类似&#xff1a; osg第三方插件的编译方法&#xff08;以jpeg插件来讲解&#xff09;。 如下为生成FFMPEG时必要的设置&#xff1a; 注意&#xff1a; 一定要…

【蓝桥杯】环形链表的约瑟夫问题

目录 题目描述&#xff1a; 输入描述&#xff1a; 输出描述&#xff1a; 示例1 解法一&#xff08;C&#xff09;&#xff1a; 解法二&#xff08;Cpp&#xff09;&#xff1a; 正文开始&#xff1a; 题目描述&#xff1a; 据说著名犹太历史学家 Josephus 有过以下故事&a…

指针详解(3)

各位少年&#xff0c;大家好&#xff0c;我是博主那一脸阳光&#xff0c;今天介绍 二级指针 指针数组&#xff0c;还有个指针数组模拟二维数组。 前言&#xff1a;在浩瀚的C语言编程宇宙中&#xff0c;指针犹如一把打开内存世界大门的独特钥匙&#xff0c;它不仅是理解程序运行…