From 99ecaca8695fc482fd351c1619e3f9ebb274af93 Mon Sep 17 00:00:00 2001
From: zhaomingwork <61895407+zhaomingwork@users.noreply.github.com>
Date: 星期一, 03 六月 2024 10:44:55 +0800
Subject: [PATCH] Add python funasr api support for websocket srv (#1777)
---
runtime/funasr_api/README.md | 72 ++++++
runtime/funasr_api/asr_example.mp3 | 0
runtime/funasr_api/asr_example.wav | 0
runtime/funasr_api/funasr_core.py | 230 ++++++++++++++++++++
runtime/funasr_api/funasr_stream.py | 72 ++++++
runtime/funasr_api/funasr_api.py | 96 ++++++++
runtime/funasr_api/example.py | 70 ++++++
runtime/funasr_api/funasr_tools.py | 84 +++++++
8 files changed, 624 insertions(+), 0 deletions(-)
diff --git a/runtime/funasr_api/README.md b/runtime/funasr_api/README.md
new file mode 100644
index 0000000..a00b445
--- /dev/null
+++ b/runtime/funasr_api/README.md
@@ -0,0 +1,72 @@
+# python funasr_api
+
+This is the api for python to use funasr engine, only support 2pass server.
+
+## For install
+
+### Install websocket-client and ffmpeg
+
+```shell
+pip install websocket-client
+apt install ffmpeg -y
+```
+
+
+#### recognizer examples
+support many audio type as ffmpeg support, detail see FunASR/runtime/funasr_api/example.py
+```shell
+ # create an recognizer
+ rcg = FunasrApi(
+ uri="wss://www.funasr.com:10096/"
+ )
+ # recognizer by filepath
+ text=rcg.rec_file("asr_example.mp3")
+ print("recognizer by filepath result=",text)
+
+
+ # recognizer by buffer
+ # rec_buf(audio_buf,ffmpeg_decode=False),set ffmpeg_decode=True if audio is not PCM or WAV type
+ with open("asr_example.wav", "rb") as f:
+ audio_bytes = f.read()
+ text=rcg.rec_buf(audio_bytes)
+ print("recognizer by buffer result=",text)
+```
+
+#### streaming recognizer examples,use FunasrApi.audio2wav to covert to WAV type if need
+
+```shell
+ rcg = FunasrApi(
+ uri="wss://www.funasr.com:10096/"
+ )
+ #define call_back function for msg
+ def on_msg(msg):
+ print("stream msg=",msg)
+ stream=rcg.create_stream(msg_callback=on_msg)
+
+ wav_path = "asr_example.wav"
+
+ with open(wav_path, "rb") as f:
+ audio_bytes = f.read()
+
+ # use FunasrApi's audio2wav to covert other audio to PCM if needed
+ #import os
+ #from funasr_tools import FunasrTools
+ #file_ext=os.path.splitext(wav_path)[-1].upper()
+ #if not file_ext =="PCM" and not file_ext =="WAV":
+ # audio_bytes=FunasrTools.audio2wav(audio_bytes)
+
+ stride = int(60 * 10 / 10 / 1000 * 16000 * 2)
+ chunk_num = (len(audio_bytes) - 1) // stride + 1
+
+ for i in range(chunk_num):
+ beg = i * stride
+ data = audio_bytes[beg : beg + stride]
+ stream.feed_chunk(data)
+ final_result=stream.wait_for_end()
+ print("asr_example.wav stream_result=",final_result)
+```
+
+## Acknowledge
+1. This project is maintained by [FunASR community](https://github.com/alibaba-damo-academy/FunASR).
+2. We acknowledge [zhaoming](https://github.com/zhaomingwork/FunASR/tree/fix_bug_for_python_websocket) for contributing the websocket service.
+3. We acknowledge [cgisky1980](https://github.com/cgisky1980/FunASR) for contributing the websocket service of offline model.
diff --git a/runtime/funasr_api/asr_example.mp3 b/runtime/funasr_api/asr_example.mp3
new file mode 100644
index 0000000..6e30e6b
--- /dev/null
+++ b/runtime/funasr_api/asr_example.mp3
Binary files differ
diff --git a/runtime/funasr_api/asr_example.wav b/runtime/funasr_api/asr_example.wav
new file mode 100644
index 0000000..bf13bb1
--- /dev/null
+++ b/runtime/funasr_api/asr_example.wav
Binary files differ
diff --git a/runtime/funasr_api/example.py b/runtime/funasr_api/example.py
new file mode 100644
index 0000000..9f932ec
--- /dev/null
+++ b/runtime/funasr_api/example.py
@@ -0,0 +1,70 @@
+"""
+ Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights
+ Reserved. MIT License (https://opensource.org/licenses/MIT)
+
+ 2023-2024 by zhaomingwork@qq.com
+"""
+
+from funasr_api import FunasrApi
+import wave
+
+def recognizer_example():
+ # create an recognizer
+ rcg = FunasrApi(
+ uri="wss://www.funasr.com:10096/"
+ )
+ # recognizer by filepath
+ text=rcg.rec_file("asr_example.mp3")
+ print("recognizer by filepath result=",text)
+
+
+ # recognizer by buffer
+ # rec_buf(audio_buf,ffmpeg_decode=False),set ffmpeg_decode=True if audio is not PCM or WAV type
+ with open("asr_example.wav", "rb") as f:
+ audio_bytes = f.read()
+ text=rcg.rec_buf(audio_bytes)
+ print("recognizer by buffer result=",text)
+
+def recognizer_stream_example():
+
+ rcg = FunasrApi(
+ uri="wss://www.funasr.com:10096/"
+ )
+ #define call_back function for msg
+ def on_msg(msg):
+ print("stream msg=",msg)
+ stream=rcg.create_stream(msg_callback=on_msg)
+
+ wav_path = "asr_example.wav"
+
+ with open(wav_path, "rb") as f:
+ audio_bytes = f.read()
+
+ # use FunasrApi's audio2wav to covert other audio to PCM if needed
+ #import os
+ #from funasr_tools import FunasrTools
+ #file_ext=os.path.splitext(wav_path)[-1].upper()
+ #if not file_ext =="PCM" and not file_ext =="WAV":
+ # audio_bytes=FunasrTools.audio2wav(audio_bytes)
+
+ stride = int(60 * 10 / 10 / 1000 * 16000 * 2)
+ chunk_num = (len(audio_bytes) - 1) // stride + 1
+
+ for i in range(chunk_num):
+ beg = i * stride
+ data = audio_bytes[beg : beg + stride]
+ stream.feed_chunk(data)
+ final_result=stream.wait_for_end()
+ print("asr_example.wav stream_result=",final_result)
+
+
+if __name__ == "__main__":
+
+ print("example for Funasr_websocket_recognizer")
+
+ recognizer_stream_example()
+
+ recognizer_example()
+
+
+
diff --git a/runtime/funasr_api/funasr_api.py b/runtime/funasr_api/funasr_api.py
new file mode 100644
index 0000000..7b932df
--- /dev/null
+++ b/runtime/funasr_api/funasr_api.py
@@ -0,0 +1,96 @@
+"""
+ Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights
+ Reserved. MIT License (https://opensource.org/licenses/MIT)
+
+ 2023-2024 by zhaomingwork@qq.com
+"""
+
+# pip install websocket-client
+# apt install ffmpeg
+
+import threading
+import traceback
+import json
+import time
+import numpy as np
+from funasr_stream import FunasrStream
+from funasr_tools import FunasrTools
+from funasr_core import FunasrCore
+# class for recognizer in websocket
+class FunasrApi:
+ """
+ python asr recognizer lib
+
+ """
+
+ def __init__(
+ self,
+ uri="wss://www.funasr.com:10096/",
+ timeout=1000,
+ msg_callback=None,
+
+ ):
+ """
+ uri: ws or wss server uri
+ msg_callback: for message received
+ timeout: timeout for get result
+ """
+ try:
+
+
+ self.uri=uri
+ self.timeout=timeout
+ self.msg_callback=msg_callback
+ self.funasr_core=None
+
+ except Exception as e:
+ print("Exception:", e)
+ traceback.print_exc()
+ def create_stream(self,msg_callback=None):
+ if self.funasr_core is not None:
+ self.funasr_core.close()
+ funasr_core=self.new_core(msg_callback=msg_callback)
+ return FunasrStream(funasr_core)
+
+
+
+
+ def new_core(self,msg_callback=None):
+ try:
+ if self.funasr_core is not None:
+ self.funasr_core.close()
+
+ if msg_callback==None:
+ msg_callback=self.msg_callback
+ funasr_core=FunasrCore(self.uri,msg_callback=msg_callback,timeout=self.timeout)
+ funasr_core.new_connection()
+ self.funasr_core=funasr_core
+ return funasr_core
+
+ except Exception as e:
+ print("init_core",e)
+ exit(0)
+
+ # rec buffer, set ffmpeg_decode=True if audio is not PCM or WAV type
+ def rec_buf(self,audio_buf,ffmpeg_decode=False):
+ try:
+ funasr_core=self.new_core()
+ funasr_core.rec_buf(audio_buf,ffmpeg_decode=ffmpeg_decode)
+ return funasr_core.get_result()
+ except Exception as e:
+ print("rec_file",e)
+ return
+ # rec file
+ def rec_file(self,file_path):
+ try:
+ funasr_core=self.new_core()
+ funasr_core.rec_file(file_path)
+ return funasr_core.get_result()
+ except Exception as e:
+ print("rec_file",e)
+ return
+
+
+
+
+
diff --git a/runtime/funasr_api/funasr_core.py b/runtime/funasr_api/funasr_core.py
new file mode 100644
index 0000000..d1cfcb0
--- /dev/null
+++ b/runtime/funasr_api/funasr_core.py
@@ -0,0 +1,230 @@
+"""
+ Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights
+ Reserved. MIT License (https://opensource.org/licenses/MIT)
+
+ 2023-2024 by zhaomingwork@qq.com
+"""
+
+# pip install websocket-client
+# apt install ffmpeg
+import ssl
+from websocket import ABNF
+from websocket import create_connection
+from queue import Queue
+import threading
+import traceback
+import json
+import time
+import numpy as np
+
+from funasr_tools import FunasrTools
+
+# class for recognizer in websocket
+class FunasrCore:
+ """
+ python asr recognizer lib
+
+ """
+
+ def __init__(
+ self,
+ uri="wss://www.funasr.com:10096/",
+ msg_callback=None,
+ timeout=1000,
+
+ ):
+ """
+ uri: ws or wss server uri
+ msg_callback: for message received
+ timeout: timeout for get result
+ """
+ try:
+ if uri.find("wss://"):
+ is_ssl=True
+ elif uri.find("ws://"):
+ is_ssl=False
+ else:
+ print("not support uri",uri)
+ exit(0)
+
+ if is_ssl == True:
+ ssl_context = ssl.SSLContext()
+ ssl_context.check_hostname = False
+ ssl_context.verify_mode = ssl.CERT_NONE
+ uri = uri
+ ssl_opt = {"cert_reqs": ssl.CERT_NONE}
+ else:
+ uri = uri
+ ssl_context = None
+ ssl_opt = None
+
+ self.ssl_opt=ssl_opt
+ self.ssl_context=ssl_context
+ self.uri = uri
+
+
+
+ print("connect to url", uri)
+
+
+
+
+
+
+ self.msg_callback=msg_callback
+ self.is_final=False
+ self.rec_text=""
+ self.timeout=timeout
+ self.rec_file_len=0
+ self.connect_state=0
+
+ except Exception as e:
+ print("Exception:", e)
+ traceback.print_exc()
+
+ def new_connection(self):
+ try:
+ self.websocket = create_connection(self.uri, ssl=self.ssl_context, sslopt=self.ssl_opt)
+
+ self.is_final=False
+ self.rec_text=""
+ self.rec_file_len=0
+ self.connect_state=0
+
+ message = json.dumps(
+ {
+ "mode": "2pass",
+ "chunk_size": [int(x) for x in "0,10,5".split(",")],
+ "encoder_chunk_look_back": 4,
+ "decoder_chunk_look_back": 1,
+ "chunk_interval": 10,
+ "wav_name": "funasr_api",
+ "is_speaking": True,
+ }
+ )
+
+ self.websocket.send(message)
+ self.connect_state=1
+ # thread for receive message
+ self.thread_msg = threading.Thread(
+ target=FunasrCore.thread_rec_msg, args=(self,)
+ )
+ self.thread_msg.start()
+
+ print("new_connection: ",message)
+ except Exception as e:
+ print("new_connection",e)
+
+
+
+ # threads for rev msg
+ def thread_rec_msg(self):
+ try:
+ while True:
+ if self.connect_state==0:
+ time.sleep(0.1)
+ continue
+ if self.connect_state==2:
+ break
+ msg = self.websocket.recv()
+
+ if msg is None or len(msg) == 0:
+ continue
+ msg = json.loads(msg)
+
+ if msg['is_final']==True:
+ self.is_final=True
+
+
+ if msg['mode']=='2pass-offline':
+ self.rec_text=self.rec_text+msg['text']
+ if not self.msg_callback is None:
+ self.msg_callback(msg)
+
+ except Exception as e:
+ #print("client closed")
+ return
+
+ # feed data to asr engine in stream way
+ def feed_chunk(self, chunk):
+ try:
+ self.websocket.send(chunk, ABNF.OPCODE_BINARY)
+ return
+ except:
+ print("feed chunk error")
+ return
+ def close(self):
+ self.connect_state==2
+ self.websocket.close()
+
+ def rec_buf(self,audio_bytes,ffmpeg_decode=False):
+ try:
+ if ffmpeg_decode:
+ audio_bytes=FunasrTools.audio2wav(audio_bytes)
+ self.rec_file_len=len(audio_bytes)
+ stride = int(60 * 10 / 10 / 1000 * 16000 * 2)
+ chunk_num = (len(audio_bytes) - 1) // stride + 1
+
+ for i in range(chunk_num):
+
+ beg = i * stride
+ data = audio_bytes[beg : beg + stride]
+ self.feed_chunk(data)
+ return self.get_result()
+ except Exception as e:
+ print("rec_file",e)
+ return
+ # rec file
+ def rec_file(self,file_path):
+ try:
+ #self.new_connection()
+ import os
+ file_ext=os.path.splitext(file_path)[-1].upper()
+
+ with open(file_path, "rb") as f:
+
+ audio_bytes = f.read()
+ if not file_ext =="PCM" and not file_ext =="WAV":
+ audio_bytes=FunasrTools.audio2wav(audio_bytes)
+ if audio_bytes==None:
+ print("error, ffmpeg can not decode such file!")
+ exit(0)
+ return self.rec_buf(audio_bytes)
+ except Exception as e:
+ print("rec_file",e)
+ return
+ def wait_for_result(self):
+ try:
+ timeout=self.timeout
+
+ file_dur=self.rec_file_len/16000/2*100
+ if file_dur>timeout:
+ timeout=file_dur
+ self.timeout=timeout
+ #print("wait_for_result timeout=",timeout)
+
+ # if file_dur==0 means in stream way and no timeout
+ while(self.is_final==False and (timeout>0 or file_dur==0 )):
+ time.sleep(0.01)
+ timeout=timeout-1
+
+ if timeout<=0 and not file_dur==0:
+ print("time out!",self.timeout)
+ except Exception as e:
+ print("wait_for_result",e)
+ return
+ def get_result(self):
+ try:
+ message = json.dumps({"is_speaking": False})
+ self.websocket.send(message)
+ self.wait_for_result()
+ self.close()
+
+ # return the msg
+ return self.rec_text
+ except Exception as e:
+ #print("get_result ",e)
+ return self.rec_text
+
+
+
diff --git a/runtime/funasr_api/funasr_stream.py b/runtime/funasr_api/funasr_stream.py
new file mode 100644
index 0000000..cd9ed39
--- /dev/null
+++ b/runtime/funasr_api/funasr_stream.py
@@ -0,0 +1,72 @@
+"""
+ Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights
+ Reserved. MIT License (https://opensource.org/licenses/MIT)
+
+ 2023-2024 by zhaomingwork@qq.com
+"""
+
+# pip install websocket-client
+# apt install ffmpeg
+
+import threading
+import traceback
+import json
+import time
+
+
+# class for recognizer in websocket
+class FunasrStream:
+ """
+ python asr recognizer lib
+
+ """
+
+ def __init__(
+ self,
+ funasr_core
+
+ ):
+ """
+ uri: ws or wss server uri
+ msg_callback: for message received
+ timeout: timeout for get result
+ """
+ try:
+ self.funasr_core=funasr_core
+
+ except Exception as e:
+ print("FunasrStream init Exception:", e)
+ traceback.print_exc()
+
+
+ # feed data to asr engine in stream way
+ def feed_chunk(self, chunk):
+ try:
+ if self.funasr_core is None:
+ print("error in stream, funasr_core is None")
+ exit(0)
+ self.funasr_core.feed_chunk(chunk)
+ return
+ except:
+ print("feed chunk error")
+ return
+
+
+
+ # return all result for this stream
+ def wait_for_end(self):
+ try:
+
+ message = json.dumps({"is_speaking": False})
+ self.funasr_core.websocket.send(message)
+ self.funasr_core.wait_for_result()
+ self.funasr_core.close()
+
+ # return the msg
+ return self.funasr_core.rec_text
+ except Exception as e:
+ print("error get_final_result ",e)
+ return ""
+
+
+
diff --git a/runtime/funasr_api/funasr_tools.py b/runtime/funasr_api/funasr_tools.py
new file mode 100644
index 0000000..6473614
--- /dev/null
+++ b/runtime/funasr_api/funasr_tools.py
@@ -0,0 +1,84 @@
+"""
+ Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights
+ Reserved. MIT License (https://opensource.org/licenses/MIT)
+
+ 2023-2024 by zhaomingwork@qq.com
+"""
+
+# pip install websocket-client
+# apt install ffmpeg
+
+
+import threading
+import traceback
+
+import time
+
+
+
+# class for recognizer in websocket
+class FunasrTools:
+ """
+ python asr recognizer lib
+
+ """
+
+ def __init__(
+ self
+
+
+ ):
+ """
+
+ """
+ try:
+
+ if FunasrTools.check_ffmpeg()==False:
+ print("pls instal ffmpeg firest, in ubuntu, you can type apt install -y ffmpeg")
+ exit(0)
+
+
+ except Exception as e:
+ print("Exception:", e)
+ traceback.print_exc()
+
+
+ # check ffmpeg installed
+ @staticmethod
+ def check_ffmpeg():
+ import subprocess
+ try:
+ subprocess.run(['ffmpeg', '-version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ return True
+ except FileNotFoundError:
+
+ return False
+ # use ffmpeg to convert audio to wav
+ @staticmethod
+ def audio2wav(audiobuf):
+ try:
+ import os
+ import subprocess
+ if FunasrTools.check_ffmpeg()==False:
+ print("pls instal ffmpeg firest, in ubuntu, you can type apt install -y ffmpeg")
+ exit(0)
+ return
+
+ ffmpeg_target_to_outwav = ["ffmpeg", "-i", '-', "-ac", "1", "-ar", "16000", "-f", "wav", "pipe:1"]
+ pipe_to = subprocess.Popen(ffmpeg_target_to_outwav,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ wavbuf, err = pipe_to.communicate(audiobuf)
+ if str(err).find("Error")>=0 or str(err).find("Unknown")>=0 or str(err).find("Invalid")>=0:
+ print("ffmpeg err",err)
+ return None
+ return wavbuf
+ except Exception as e:
+ print("audio2wav",e)
+ return None
+
+
+
+
+
--
Gitblit v1.9.1