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




















