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.mzph.cn/news/664447.shtml

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

相关文章

Unity_Playable工具使用

Unity_Playable工具使用 目录 Unity_Playable工具使用 1. Default Playables(Timeline扩展) 2. PlayableGraph Visualizer&#x

armbian修改docker目录到硬盘

玩客云自带内存8G&#xff0c;根目录很快就满了&#xff0c;这里调整docker的目录到硬盘上/sda1。 docker info|grep "Docker Root Dir:" Docker Root Dir:/var/lib/docker 查看docker 默认目录在哪里 Docker 版本 > v17.05.0 docker -v Docker version 25.0.…

elementUI中el-tree组件单选没有复选框时,选中、current-node-key高亮、刷新后保留展开状态功能的实现

目录 一、代码实现1. 属性了解 &#xff08;[更多](https://element.eleme.cn/#/zh-CN/component/tree)&#xff09;2. 实现步骤3.代码示例 二、 效果图 一、代码实现 1. 属性了解 &#xff08;更多&#xff09; node-key 每个树节点用来作为唯一标识的属性&#xff0c;整棵树…

Linux 磁盘空间占用率100%的排查

&#x1f4d1;前言 使用 Linux 操作系统时&#xff0c;可能会遇到磁盘空间不足的错误&#xff0c;这种错误通常会导致系统运行缓慢或崩溃。本文将介绍磁盘排查的方法。⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1…

rust gui开发框架选择

作为一个系统编程强大语言&#xff0c;怎么能少得了图形界面的开发 实际上写这篇前我也不知道&#xff0c;于是我问了ai大模型&#xff0c;文心3.5和chatgpt4.0 答案实际上不能满意&#xff0c;最后我做了下筛选 参考博文&#xff1a; rust开发环境配置&#xff1a;链接 一、…

使用websocket建立长链接实现用户点对点即时通讯

WebSocket是一种网络传输协议&#xff0c;位于OSI模型的应用层&#xff0c;设计用于在单个TCP连接上进行全双工通信。 WebSocket的主要特点包括&#xff1a; 全双工通信&#xff1a;WebSocket允许服务器和客户端之间进行双向实时通信。这与传统的HTTP请求不同&#xff0c;后者…

MacOS安装JDK+Maven+Idea插件+nvm等

Java安装环境(MacOS)JDKMavenIdea插件nvm等 背景&#xff1a;新机安装开发环境发现需要找很多文章&#xff0c;&#xff0c;&#xff0c;&#xff0c;这里一篇文章安装所有环境 文章目录 Java安装环境(MacOS)JDKMavenIdea插件nvm等一、安装JDK①&#xff1a;下载②&#xff1a;…

阿里云a10GPU,centos7,cuda11.2环境配置

Anaconda3-2022.05-Linux-x86_64.sh gcc升级 centos7升级gcc至8.2_centos7 yum gcc8.2.0-CSDN博客 paddlepaddle python -m pip install paddlepaddle-gpu2.5.1.post112 -f https://www.paddlepaddle.org.cn/whl/linux/mkl/avx/stable.html 报错 ImportError: libssl.so…

C++ pair+map+set+multimap+multiset+AVL树+红黑树(深度剖析)

文章目录 1. 前言2. 关联式容器3. pair——键值对4. 树形结构的关联式容器4.1 set4.1.1 set 的介绍4.1.2 set 的使用 4.2 map4.2.1 map 的介绍4.2.2 map 的使用 4.3 multiset4.3.1 multiset 的介绍4.3.2 multiset 的使用 4.4 multimap4.4.1 multimap 的介绍4.4.2 multimap 的使…

Latex学习记录

目录 1.Latex各种箭头符号总结 2.[Latex]公式编辑&#xff0c;编号、对齐 3.Latex公式编号: 多行公式多编号&#xff0c;多行公式单编号 4.LaTex中输入空格以及换行 1.Latex各种箭头符号总结 箭头符号 - ➚ (piliapp.com)https://cn.piliapp.com/symbol/arrow/Latex各种箭头…

Windows巧用Git实现笔记自动备份

随着数字时代的来临&#xff0c;数据安全和备份变得尤为重要。特别是对于经常写笔记或需要整理资料的人来说&#xff0c;如何自动备份这些宝贵的信息成为了一个关键问题。今天&#xff0c;我们将探讨如何使用Git在Windows上实现笔记的自动备份。 一、Git简介 Git是一个分布式…

Flutter 开发3:创建第一个Flutter应用

Step 1: 安装Flutter 1.1 下载Flutter SDK 首先&#xff0c;你需要访问Flutter官方网站下载最新的Flutter SDK。选择适合你操作系统的安装包。 $ cd ~/development $ unzip ~/Downloads/flutter_macos_2.2.3-stable.zip1.2 更新环境变量 接下来&#xff0c;你需要将Flutter…

Spring设计模式之单例模式

文章目录 一、概述二、单例模式的优点三、Spring中的单例模式四、单例模式的实现方式五、总结 一、概述 单例模式是一种创建型设计模式&#xff0c;确保一个类只有一个实例&#xff0c;并提供一个全局访问点来获取该实例。在Spring框架中&#xff0c;单例模式是默认的Bean定义…

网络异常案例四_IP异常

问题现象 终端设备离线&#xff0c;现场根据设备ip&#xff0c;ping不通。查看路由器。 同一个路由器显示的终端设备&#xff08;走同一个wifi模块接入&#xff09;&#xff0c;包含不同网段的ip。 现场是基于三层的无线漫游&#xff0c;多个路由器wifi配置了相同的ssid信息&a…

springBoot静态资源文件夹以及文件夹之间的优先级

1、springBoot静态资源文件夹&#xff0c;系统默认路径&#xff0c;优先级由大到小 classpath:/META-INF/resources/ classpath:/resources/ classpath:/static/ classpath:/public/ 比如当static文件夹中和public文件夹中都存在a.html 浏览器访问localhost:8080/a.html将访问…

ref和reactive

看尤雨溪说&#xff1a;为什么Vue3 中应该使用 Ref 而不是 Reactive&#xff1f;

C的文件操作

1.栈区上的空间运行效率很高 堆区分配的空间一般由程序员主动释放 静态区的话程序运行结束后由系统释放 2.根据数据的组织形式&#xff0c;数据文件被称为文本文件或者二进制文件 数据在内存中以二进制的形式存储&#xff0c;如果不加转换的输出到外存的文件中&#xff0c;…

pintia6-2符号函数 6-1两点距离

pintia的函数题&#xff0c;只需要把函数写上去就可以了&#xff0c;6-2函数题比较简单&#xff0c;三个if就可以解决: 6-1则套用数学公式即可&#xff0c;注意把函数名复制粘贴过去&#xff0c;以免抄错

2024年华为OD机试真题-虚拟游戏理财-Java-OD统一考试(C卷)

题目描述: 在一款虚拟游戏中生活,你必须进行投资以增强在虚拟游戏中的资产以免被淘汰出局。现有一家Bank,它提供有若干理财产品m,风险及投资回报不同,你有N(元)进行投资,能接受的总风险值为X。 你要在可接受范围内选择最优的投资方式获得最大回报。 说明: 在虚拟游戏中…

安装 kvm Qemu rocky8 linux

在基于 Red Hat 的 Linux 发行版&#xff08;如 CentOS 或 Rocky Linux&#xff09;上安装 KVM&#xff08;Kernel-based Virtual Machine&#xff09;&#xff0c;您需要遵循以下步骤&#xff1a; 检查 CPU 虚拟化支持&#xff1a; 确保您的 CPU 支持硬件虚拟化。您可以通过运…