冯杨

豆包大模型,名称赋予

音频识别asr:使用本地方案funasr(复用的Fay项目中的funasr)
FunASR服务连接测试脚本
用于验证本地FunASR WebSocket服务是否可以正常连接
webrtcapichat.html中对话框做进一步调整,侧边栏增加对话框的透明度调整。暂时设置对话框的背景色差异大些,美学设计暂不考虑。对话框支持隐藏
  1 +from threading import Thread
  2 +from threading import Lock
  3 +import websocket
  4 +import json
  5 +import time
  6 +import ssl
  7 +import wave
  8 +import _thread as thread
  9 +from aliyunsdkcore.client import AcsClient
  10 +from aliyunsdkcore.request import CommonRequest
  11 +
  12 +from core import wsa_server
  13 +from scheduler.thread_manager import MyThread
  14 +from utils import util
  15 +from utils import config_util as cfg
  16 +from core.authorize_tb import Authorize_Tb
  17 +
  18 +__running = True
  19 +__my_thread = None
  20 +_token = ''
  21 +
  22 +
  23 +def __post_token():
  24 + global _token
  25 + __client = AcsClient(
  26 + cfg.key_ali_nls_key_id,
  27 + cfg.key_ali_nls_key_secret,
  28 + "cn-shanghai"
  29 + )
  30 +
  31 + __request = CommonRequest()
  32 + __request.set_method('POST')
  33 + __request.set_domain('nls-meta.cn-shanghai.aliyuncs.com')
  34 + __request.set_version('2019-02-28')
  35 + __request.set_action_name('CreateToken')
  36 + info = json.loads(__client.do_action_with_exception(__request))
  37 + _token = info['Token']['Id']
  38 + authorize = Authorize_Tb()
  39 + authorize_info = authorize.find_by_userid(cfg.key_ali_nls_key_id)
  40 + if authorize_info is not None:
  41 + authorize.update_by_userid(cfg.key_ali_nls_key_id, _token, info['Token']['ExpireTime']*1000)
  42 + else:
  43 + authorize.add(cfg.key_ali_nls_key_id, _token, info['Token']['ExpireTime']*1000)
  44 +
  45 +def __runnable():
  46 + while __running:
  47 + __post_token()
  48 + time.sleep(60 * 60 * 12)
  49 +
  50 +
  51 +def start():
  52 + MyThread(target=__runnable).start()
  53 +
  54 +
  55 +class ALiNls:
  56 + # 初始化
  57 + def __init__(self, username):
  58 + self.__URL = 'wss://nls-gateway-cn-shenzhen.aliyuncs.com/ws/v1'
  59 + self.__ws = None
  60 + self.__frames = []
  61 + self.started = False
  62 + self.__closing = False
  63 + self.__task_id = ''
  64 + self.done = False
  65 + self.finalResults = ""
  66 + self.username = username
  67 + self.data = b''
  68 + self.__endding = False
  69 + self.__is_close = False
  70 + self.lock = Lock()
  71 +
  72 + def __create_header(self, name):
  73 + if name == 'StartTranscription':
  74 + self.__task_id = util.random_hex(32)
  75 + header = {
  76 + "appkey": cfg.key_ali_nls_app_key,
  77 + "message_id": util.random_hex(32),
  78 + "task_id": self.__task_id,
  79 + "namespace": "SpeechTranscriber",
  80 + "name": name
  81 + }
  82 + return header
  83 +
  84 + # 收到websocket消息的处理
  85 + def on_message(self, ws, message):
  86 + try:
  87 + data = json.loads(message)
  88 + header = data['header']
  89 + name = header['name']
  90 + if name == 'TranscriptionStarted':
  91 + self.started = True
  92 + if name == 'SentenceEnd':
  93 + self.done = True
  94 + self.finalResults = data['payload']['result']
  95 + if wsa_server.get_web_instance().is_connected(self.username):
  96 + wsa_server.get_web_instance().add_cmd({"panelMsg": self.finalResults, "Username" : self.username})
  97 + if wsa_server.get_instance().is_connected_human(self.username):
  98 + content = {'Topic': 'human', 'Data': {'Key': 'log', 'Value': self.finalResults}, 'Username' : self.username}
  99 + wsa_server.get_instance().add_cmd(content)
  100 + ws.close()#TODO
  101 + elif name == 'TranscriptionResultChanged':
  102 + self.finalResults = data['payload']['result']
  103 + if wsa_server.get_web_instance().is_connected(self.username):
  104 + wsa_server.get_web_instance().add_cmd({"panelMsg": self.finalResults, "Username" : self.username})
  105 + if wsa_server.get_instance().is_connected_human(self.username):
  106 + content = {'Topic': 'human', 'Data': {'Key': 'log', 'Value': self.finalResults}, 'Username' : self.username}
  107 + wsa_server.get_instance().add_cmd(content)
  108 +
  109 + except Exception as e:
  110 + print(e)
  111 + # print("### message:", message)
  112 +
  113 + # 收到websocket的关闭要求
  114 + def on_close(self, ws, code, msg):
  115 + self.__endding = True
  116 + self.__is_close = True
  117 +
  118 + # 收到websocket错误的处理
  119 + def on_error(self, ws, error):
  120 + print("aliyun asr error:", error)
  121 + self.started = True #避免在aliyun asr出错时,recorder一直等待start状态返回
  122 +
  123 + # 收到websocket连接建立的处理
  124 + def on_open(self, ws):
  125 + self.__endding = False
  126 + #为了兼容多路asr,关闭过程数据
  127 + def run(*args):
  128 + while self.__endding == False:
  129 + try:
  130 + if len(self.__frames) > 0:
  131 + with self.lock:
  132 + frame = self.__frames.pop(0)
  133 + if isinstance(frame, dict):
  134 + ws.send(json.dumps(frame))
  135 + elif isinstance(frame, bytes):
  136 + ws.send(frame, websocket.ABNF.OPCODE_BINARY)
  137 + self.data += frame
  138 + else:
  139 + time.sleep(0.001) # 避免忙等
  140 + except Exception as e:
  141 + print(e)
  142 + break
  143 + if self.__is_close == False:
  144 + for frame in self.__frames:
  145 + ws.send(frame, websocket.ABNF.OPCODE_BINARY)
  146 + frame = {"header": self.__create_header('StopTranscription')}
  147 + ws.send(json.dumps(frame))
  148 + thread.start_new_thread(run, ())
  149 +
  150 + def __connect(self):
  151 + self.finalResults = ""
  152 + self.done = False
  153 + with self.lock:
  154 + self.__frames.clear()
  155 + self.__ws = websocket.WebSocketApp(self.__URL + '?token=' + _token, on_message=self.on_message)
  156 + self.__ws.on_open = self.on_open
  157 + self.__ws.on_error = self.on_error
  158 + self.__ws.on_close = self.on_close
  159 + self.__ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
  160 +
  161 + def send(self, buf):
  162 + with self.lock:
  163 + self.__frames.append(buf)
  164 +
  165 + def start(self):
  166 + Thread(target=self.__connect, args=[]).start()
  167 + data = {
  168 + 'header': self.__create_header('StartTranscription'),
  169 + "payload": {
  170 + "format": "pcm",
  171 + "sample_rate": 16000,
  172 + "enable_intermediate_result": True,
  173 + "enable_punctuation_prediction": False,
  174 + "enable_inverse_text_normalization": True,
  175 + "speech_noise_threshold": -1
  176 + }
  177 + }
  178 + self.send(data)
  179 +
  180 + def end(self):
  181 + self.__endding = True
  182 + with wave.open('cache_data/input2.wav', 'wb') as wf:
  183 + # 设置音频参数
  184 + n_channels = 1 # 单声道
  185 + sampwidth = 2 # 16 位音频,每个采样点 2 字节
  186 + wf.setnchannels(n_channels)
  187 + wf.setsampwidth(sampwidth)
  188 + wf.setframerate(16000)
  189 + wf.writeframes(self.data)
  190 + self.data = b''
  1 +# -*- coding: utf-8 -*-
  2 +"""
  3 +AIfeng/2025-01-27
  4 +FunASR WebSocket客户端 - 兼容性包装器
  5 +基于新的FunASRClient实现
  6 +"""
  7 +
  8 +import sys
  9 +import os
  10 +
  11 +# 添加项目根目录到路径
  12 +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
  13 +
  14 +from funasr_asr import FunASRClient
  15 +# 修复导入路径
  16 +try:
  17 + from core import wsa_server
  18 +except ImportError:
  19 + # 如果core模块不存在,创建一个模拟的wsa_server
  20 + class MockWSAServer:
  21 + def get_web_instance(self):
  22 + return MockWebInstance()
  23 + def get_instance(self):
  24 + return MockInstance()
  25 +
  26 + class MockWebInstance:
  27 + def is_connected(self, username):
  28 + return False
  29 + def add_cmd(self, cmd):
  30 + print(f"Mock Web: {cmd}")
  31 +
  32 + class MockInstance:
  33 + def is_connected_human(self, username):
  34 + return False
  35 + def add_cmd(self, cmd):
  36 + print(f"Mock Human: {cmd}")
  37 +
  38 + wsa_server = MockWSAServer()
  39 +
  40 +try:
  41 + from utils import config_util as cfg
  42 +except ImportError:
  43 + # 使用项目根目录的config_util
  44 + import config_util as cfg
  45 +
  46 +try:
  47 + from utils import util
  48 +except ImportError:
  49 + # 使用项目根目录的util
  50 + import util
  51 +
  52 +class FunASR:
  53 + """FunASR兼容性包装器"""
  54 +
  55 + def __init__(self, username):
  56 + # 创建一个简单的选项对象
  57 + class SimpleOpt:
  58 + def __init__(self, username):
  59 + self.username = username
  60 +
  61 + opt = SimpleOpt(username)
  62 + self.client = FunASRClient(opt)
  63 + self.username = username
  64 + self.__connected = False
  65 + self.__frames = []
  66 + self.__state = 0
  67 + self.__closing = False
  68 + self.__task_id = ''
  69 + self.done = False
  70 + self.finalResults = ""
  71 + self.__reconnect_delay = 1
  72 + self.__reconnecting = False
  73 + self.started = True
  74 +
  75 + # 消息处理回调
  76 + self.on_message_callback = None
  77 +
  78 + # 设置结果回调
  79 + self.client.set_result_callback(self._handle_result)
  80 +
  81 + def set_message_callback(self, callback):
  82 + """设置消息回调函数"""
  83 + self.on_message_callback = callback
  84 +
  85 + def _handle_result(self, message):
  86 + """处理识别结果的内部方法"""
  87 + try:
  88 + self.done = True
  89 + self.finalResults = message
  90 +
  91 + # 调用用户设置的回调函数
  92 + if self.on_message_callback:
  93 + self.on_message_callback(message)
  94 +
  95 + if wsa_server.get_web_instance().is_connected(self.username):
  96 + wsa_server.get_web_instance().add_cmd({"panelMsg": self.finalResults, "Username" : self.username})
  97 + if wsa_server.get_instance().is_connected_human(self.username):
  98 + content = {'Topic': 'human', 'Data': {'Key': 'log', 'Value': self.finalResults}, 'Username' : self.username}
  99 + wsa_server.get_instance().add_cmd(content)
  100 + except Exception as e:
  101 + print(e)
  102 +
  103 + # 兼容性方法
  104 + def on_message(self, ws, message):
  105 + """兼容性方法 - 收到websocket消息的处理"""
  106 + self._handle_result(message)
  107 +
  108 + def on_close(self, ws, code, msg):
  109 + """兼容性方法 - 收到websocket错误的处理"""
  110 + self.__connected = False
  111 + # util.printInfo(1, self.username, f"### CLOSE:{msg}")
  112 +
  113 + def on_error(self, ws, error):
  114 + """兼容性方法 - 收到websocket错误的处理"""
  115 + self.__connected = False
  116 + # util.printInfo(1, self.username, f"### error:{error}")
  117 +
  118 + def on_open(self, ws):
  119 + """兼容性方法 - 收到websocket连接建立的处理"""
  120 + self.__connected = True
  121 +
  122 + def add_frame(self, frame):
  123 + """兼容性方法 - 添加音频帧"""
  124 + if isinstance(frame, bytes):
  125 + self.client.send_audio(frame)
  126 + else:
  127 + # 对于字典类型的控制消息,暂时忽略
  128 + pass
  129 +
  130 + def send(self, buf):
  131 + """兼容性方法 - 发送音频数据"""
  132 + if isinstance(buf, bytes):
  133 + self.client.send_audio(buf)
  134 +
  135 + def send_url(self, url):
  136 + """兼容性方法 - 发送URL(新客户端不支持此功能)"""
  137 + print(f"警告: send_url功能在新的FunASR客户端中不支持: {url}")
  138 +
  139 + def start(self):
  140 + """兼容性方法 - 启动识别"""
  141 + self.client.start_recognition()
  142 + self.__connected = True
  143 + self.done = False
  144 + self.finalResults = ""
  145 +
  146 + def end(self):
  147 + """兼容性方法 - 结束识别"""
  148 + self.client.stop_recognition()
  149 + self.__closing = True
  150 + self.__connected = False
  1 +import pyaudio
  2 +import websockets
  3 +import asyncio
  4 +from queue import Queue
  5 +import argparse
  6 +import json
  7 +
  8 +parser = argparse.ArgumentParser()
  9 +parser.add_argument("--host", type=str, default="127.0.0.1", required=False, help="host ip, localhost, 0.0.0.0")
  10 +parser.add_argument("--port", type=int, default=10197, required=False, help="grpc server port")
  11 +parser.add_argument("--chunk_size", type=int, default=160, help="ms")
  12 +parser.add_argument("--vad_needed", type=bool, default=True)
  13 +args = parser.parse_args()
  14 +
  15 +voices = Queue()
  16 +
  17 +async def record():
  18 + global voices
  19 + FORMAT = pyaudio.paInt16
  20 + CHANNELS = 1
  21 + RATE = 16000
  22 + CHUNK = int(RATE / 1000 * args.chunk_size)
  23 +
  24 + p = pyaudio.PyAudio()
  25 +
  26 + stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK)
  27 +
  28 + while True:
  29 + data = stream.read(CHUNK)
  30 + voices.put(data)
  31 + await asyncio.sleep(0.01)
  32 +
  33 +async def ws_send(websocket):
  34 + global voices
  35 + print("Started sending data!")
  36 + data_head = {
  37 + 'vad_need': args.vad_needed,
  38 + 'state': ''
  39 + }
  40 + await websocket.send(json.dumps(data_head))
  41 +
  42 + while True:
  43 + while not voices.empty():
  44 + data = voices.get()
  45 + voices.task_done()
  46 + try:
  47 + await websocket.send(data)
  48 + except Exception as e:
  49 + print('Exception occurred:', e)
  50 + return # Return to attempt reconnection
  51 + await asyncio.sleep(0.01)
  52 +
  53 +async def message(websocket):
  54 + while True:
  55 + try:
  56 + print(await websocket.recv())
  57 + except Exception as e:
  58 + print("Exception:", e)
  59 + return # Return to attempt reconnection
  60 +
  61 +async def ws_client():
  62 + uri = "ws://{}:{}".format(args.host, args.port)
  63 + while True:
  64 + try:
  65 + async with websockets.connect(uri, subprotocols=["binary"], ping_interval=None) as websocket:
  66 + task1 = asyncio.create_task(record())
  67 + task2 = asyncio.create_task(ws_send(websocket))
  68 + task3 = asyncio.create_task(message(websocket))
  69 + await asyncio.gather(task1, task2, task3)
  70 + except Exception as e:
  71 + print("WebSocket connection failed: ", e)
  72 + await asyncio.sleep(5) # Wait for 5 seconds before trying to reconnect
  73 +
  74 +asyncio.get_event_loop().run_until_complete(ws_client())
  1 +import asyncio
  2 +import websockets
  3 +import argparse
  4 +import json
  5 +import logging
  6 +from funasr import AutoModel
  7 +import os
  8 +
  9 +# 设置日志级别
  10 +logger = logging.getLogger(__name__)
  11 +logger.setLevel(logging.CRITICAL)
  12 +
  13 +# 解析命令行参数
  14 +parser = argparse.ArgumentParser()
  15 +parser.add_argument("--host", type=str, default="0.0.0.0", help="host ip, localhost, 0.0.0.0")
  16 +parser.add_argument("--port", type=int, default=10197, help="grpc server port")
  17 +parser.add_argument("--ngpu", type=int, default=1, help="0 for cpu, 1 for gpu")
  18 +parser.add_argument("--gpu_id", type=int, default=0, help="specify which gpu device to use")
  19 +args = parser.parse_args()
  20 +
  21 +# 初始化模型
  22 +print("model loading")
  23 +asr_model = AutoModel(model="paraformer-zh", model_revision="v2.0.4",
  24 + vad_model="fsmn-vad", vad_model_revision="v2.0.4",
  25 + punc_model="ct-punc-c", punc_model_revision="v2.0.4",
  26 + device=f"cuda:{args.gpu_id}" if args.ngpu else "cpu", disable_update=True)
  27 + # ,disable_update=True
  28 +print("model loaded")
  29 +websocket_users = {}
  30 +task_queue = asyncio.Queue()
  31 +
  32 +async def ws_serve(websocket, path):
  33 + global websocket_users
  34 + user_id = id(websocket)
  35 + websocket_users[user_id] = websocket
  36 + try:
  37 + async for message in websocket:
  38 + if isinstance(message, str):
  39 + data = json.loads(message)
  40 + if 'url' in data:
  41 + await task_queue.put((websocket, data['url']))
  42 + except websockets.exceptions.ConnectionClosed as e:
  43 + logger.info(f"Connection closed: {e.reason}")
  44 + except Exception as e:
  45 + logger.error(f"Unexpected error: {e}")
  46 + finally:
  47 + logger.info(f"Cleaning up connection for user {user_id}")
  48 + if user_id in websocket_users:
  49 + del websocket_users[user_id]
  50 + await websocket.close()
  51 + logger.info("WebSocket closed")
  52 +
  53 +async def worker():
  54 + while True:
  55 + websocket, url = await task_queue.get()
  56 + if websocket.open:
  57 + await process_wav_file(websocket, url)
  58 + else:
  59 + logger.info("WebSocket connection is already closed when trying to process file")
  60 + task_queue.task_done()
  61 +
  62 +async def process_wav_file(websocket, url):
  63 + # 热词
  64 + param_dict = {"sentence_timestamp": False}
  65 + with open("data/hotword.txt", "r", encoding="utf-8") as f:
  66 + lines = f.readlines()
  67 + lines = [line.strip() for line in lines]
  68 + hotword = " ".join(lines)
  69 + print(f"热词:{hotword}")
  70 + param_dict["hotword"] = hotword
  71 + wav_path = url
  72 + try:
  73 + res = asr_model.generate(input=wav_path, is_final=True, **param_dict)
  74 + if res:
  75 + if 'text' in res[0] and websocket.open:
  76 + await websocket.send(res[0]['text'])
  77 + except Exception as e:
  78 + print(f"Error during model.generate: {e}")
  79 + finally:
  80 + if os.path.exists(wav_path):
  81 + os.remove(wav_path)
  82 +
  83 +async def main():
  84 + server = await websockets.serve(ws_serve, args.host, args.port, ping_interval=10)
  85 + worker_task = asyncio.create_task(worker())
  86 +
  87 + try:
  88 + # 保持服务器运行,直到被手动中断
  89 + print(f"ASR服务器已启动,监听地址: {args.host}:{args.port}")
  90 + await asyncio.Future() # 永久等待,直到程序被中断
  91 + except asyncio.CancelledError:
  92 + print("服务器正在关闭...")
  93 + finally:
  94 + # 清理资源
  95 + worker_task.cancel()
  96 + try:
  97 + await worker_task
  98 + except asyncio.CancelledError:
  99 + pass
  100 + server.close()
  101 + await server.wait_closed()
  102 +
  103 +# 使用 asyncio 运行主函数
  104 +asyncio.run(main())
  1 +## 语音服务介绍
  2 +
  3 +该服务以modelscope funasr语音识别为基础
  4 +
  5 +
  6 +## Install
  7 +pip install torch -i https://mirrors.aliyun.com/pypi/simple/
  8 +pip install modelscope -i https://mirrors.aliyun.com/pypi/simple/
  9 +pip install testresources -i https://mirrors.aliyun.com/pypi/simple/
  10 +pip install websockets -i https://mirrors.aliyun.com/pypi/simple/
  11 +pip install torchaudio -i https://mirrors.aliyun.com/pypi/simple/
  12 +pip install FunASR -i https://mirrors.aliyun.com/pypi/simple/
  13 +
  14 +## Start server
  15 +
  16 +2、python -u ASR_server.py --host "0.0.0.0" --port 10197 --ngpu 0
  17 +
  18 +
  19 +
  20 +## Fay connect
  21 +更改fay/system.conf配置项,并重新启动fay.
  22 +
  23 +https://www.bilibili.com/video/BV1qs4y1g74e/?share_source=copy_web&vd_source=64cd9062f5046acba398177b62bea9ad
  24 +
  25 +## Acknowledge
  26 +感谢
  27 +1. 中科大脑算法工程师张聪聪
  28 +2. [cgisky1980](https://github.com/cgisky1980/FunASR)
  29 +3. [modelscope](https://github.com/modelscope/modelscope)
  30 +4. [FunASR](https://github.com/alibaba-damo-academy/FunASR)
  31 +5. [Fay数字人助理](https://github.com/TheRamU/Fay).
  32 +
  33 +--------------------------------------------------------------------------------------
  34 +
  35 +GPU服务器部署:
  36 +GPU服务器局域网地址:10.110.3.219
  37 +可用GPU:1号GPU
  38 +python -u ASR_server.py --host "10.110.3.219" --port 10197 --ngpu 1 --gpu_id 1
  39 +
  40 +python -u ASR_server.py --host "127.0.0.1" --port 10197 --ngpu 1
  41 +
  42 +lsof -i :10197
  43 +
  44 +#!/bin/bash
  45 +source /home/fengyang/anaconda3/bin/activate livetalking
  46 +
  47 +nohup python /home/fengyang/controlPanel/main.py >> /home/fengyang/controlPanel/panel_logfile.log 2>&1 &
  48 +
  49 +
  50 +
  51 +chmod +x serverFunasr.sh
  1 +'''
  2 + Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights
  3 + Reserved. MIT License (https://opensource.org/licenses/MIT)
  4 +
  5 + 2022-2023 by zhaomingwork@qq.com
  6 +'''
  7 +# pip install websocket-client
  8 +import ssl
  9 +from websocket import ABNF
  10 +from websocket import create_connection
  11 +from queue import Queue
  12 +import threading
  13 +import traceback
  14 +import json
  15 +import time
  16 +import numpy as np
  17 +
  18 +import pyaudio
  19 +import asyncio
  20 +import argparse
  21 +
  22 +# class for recognizer in websocket
  23 +class Funasr_websocket_recognizer():
  24 + '''
  25 + python asr recognizer lib
  26 +
  27 + '''
  28 +
  29 + parser = argparse.ArgumentParser()
  30 + parser.add_argument("--host", type=str, default="127.0.0.1", required=False, help="host ip, localhost, 0.0.0.0")
  31 + parser.add_argument("--port", type=int, default=10194, required=False, help="grpc server port")
  32 + parser.add_argument("--chunk_size", type=int, default=160, help="ms")
  33 + parser.add_argument("--vad_needed", type=bool, default=True)
  34 + args = parser.parse_args()
  35 +
  36 + def __init__(self, host="127.0.0.1",
  37 + port="10197",
  38 + is_ssl=True,
  39 + chunk_size="0, 10, 5",
  40 + chunk_interval=10,
  41 + mode="2pass",
  42 + wav_name="default"):
  43 + '''
  44 + host: server host ip
  45 + port: server port
  46 + is_ssl: True for wss protocal, False for ws
  47 + '''
  48 + try:
  49 + if is_ssl == True:
  50 + ssl_context = ssl.SSLContext()
  51 + ssl_context.check_hostname = False
  52 + ssl_context.verify_mode = ssl.CERT_NONE
  53 + uri = "wss://{}:{}".format(host, port)
  54 + ssl_opt={"cert_reqs": ssl.CERT_NONE}
  55 + else:
  56 + uri = "ws://{}:{}".format(host, port)
  57 + ssl_context = None
  58 + ssl_opt=None
  59 + self.host = host
  60 + self.port = port
  61 +
  62 + self.msg_queue = Queue() # used for recognized result text
  63 +
  64 + print("connect to url",uri)
  65 + self.websocket=create_connection(uri, ssl=ssl_context, sslopt=ssl_opt)
  66 +
  67 + self.thread_msg = threading.Thread(target=Funasr_websocket_recognizer.thread_rec_msg, args=(self,))
  68 + self.thread_msg.start()
  69 + chunk_size = [int(x) for x in chunk_size.split(",")]
  70 + stride = int(60 * chunk_size[1] / chunk_interval / 1000 * 16000 * 2)
  71 + chunk_num = (len(audio_bytes) - 1) // stride + 1
  72 +
  73 + message = json.dumps({"mode": mode,
  74 + "chunk_size": chunk_size,
  75 + "encoder_chunk_look_back": 4,
  76 + "decoder_chunk_look_back": 1,
  77 + "chunk_interval": chunk_interval,
  78 + "wav_name": wav_name,
  79 + "is_speaking": True})
  80 +
  81 + self.websocket.send(message)
  82 +
  83 + print("send json",message)
  84 +
  85 + except Exception as e:
  86 + print("Exception:", e)
  87 + traceback.print_exc()
  88 +
  89 + # async def record():
  90 + # global voices
  91 + # FORMAT = pyaudio.paInt16
  92 + # CHANNELS = 1
  93 + # RATE = 16000
  94 + # CHUNK = int(RATE / 1000 * args.chunk_size)
  95 +
  96 + # p = pyaudio.PyAudio()
  97 +
  98 + # stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK)
  99 +
  100 + # while True:
  101 + # data = stream.read(CHUNK)
  102 + # voices.put(data)
  103 + # await asyncio.sleep(0.01)
  104 +
  105 +
  106 + # threads for rev msg
  107 + def thread_rec_msg(self):
  108 + try:
  109 + while(True):
  110 + msg=self.websocket.recv()
  111 + if msg is None or len(msg) == 0:
  112 + continue
  113 + msg = json.loads(msg)
  114 +
  115 + self.msg_queue.put(msg)
  116 + except Exception as e:
  117 + print("client closed")
  118 +
  119 + # feed data to asr engine, wait_time means waiting for result until time out
  120 + def feed_chunk(self, chunk, wait_time=0.01):
  121 + try:
  122 + self.websocket.send(chunk, ABNF.OPCODE_BINARY)
  123 + # loop to check if there is a message, timeout in 0.01s
  124 + while(True):
  125 + msg = self.msg_queue.get(timeout=wait_time)
  126 + if self.msg_queue.empty():
  127 + break
  128 +
  129 + return msg
  130 + except:
  131 + return ""
  132 +
  133 + def close(self,timeout=1):
  134 + message = json.dumps({"is_speaking": False})
  135 + self.websocket.send(message)
  136 + # sleep for timeout seconds to wait for result
  137 + time.sleep(timeout)
  138 + msg=""
  139 + while(not self.msg_queue.empty()):
  140 + msg = self.msg_queue.get()
  141 +
  142 + self.websocket.close()
  143 + # only resturn the last msg
  144 + return msg
  145 +
  146 +if __name__ == '__main__':
  147 +
  148 + print('example for Funasr_websocket_recognizer')
  149 + import wave
  150 + wav_path = "long.wav"
  151 + # wav_path = "/Users/zhifu/Downloads/modelscope_models/speech_seaco_paraformer_large_asr_nat-zh-cn-16k-common-vocab8404-pytorch/example/asr_example.wav"
  152 + with wave.open(wav_path, "rb") as wav_file:
  153 + params = wav_file.getparams()
  154 + frames = wav_file.readframes(wav_file.getnframes())
  155 + audio_bytes = bytes(frames)
  156 +
  157 +
  158 + stride = int(60 * 10 / 10 / 1000 * 16000 * 2)
  159 + chunk_num = (len(audio_bytes) - 1) // stride + 1
  160 + # create an recognizer
  161 + rcg = Funasr_websocket_recognizer()
  162 + # loop to send chunk
  163 + for i in range(chunk_num):
  164 +
  165 + beg = i * stride
  166 + data = audio_bytes[beg:beg + stride]
  167 +
  168 + text = rcg.feed_chunk(data,wait_time=0.02)
  169 + if len(text)>0:
  170 + print("text",text)
  171 + time.sleep(0.05)
  172 +
  173 + # get last message
  174 + text = rcg.close(timeout=3)
  175 + print("text",text)
  176 +
  177 +
  178 +