1+ from wave import Error
2+ from maix import camera , display , app , time , image , audio
3+ from maix ._maix .err import Err
4+ import asyncio , json , websockets
5+ import numpy as np
6+
7+ STATUS_OK = 0
8+ STATUS_EXIT = 1
9+ STATUS_SCAN_QRCODE = 2
10+ STATUS_SHOW_ASR = 3
11+
12+ class AsrOnlineClient :
13+ def __init__ (self , exit_event ):
14+ self .recorder = audio .Recorder (sample_rate = 16000 , channel = 1 )
15+ self .recorder .volume (100 )
16+ self .audio_queue = asyncio .Queue ()
17+ self .result_queue = asyncio .Queue ()
18+ self .exit_event = exit_event
19+ pass
20+
21+ async def read_audio_queue (self ):
22+ while not app .need_exit ():
23+ indata , status = await self .audio_queue .get ()
24+ yield indata , status
25+
26+ async def read_asr_result (self ):
27+ while not app .need_exit ():
28+ res , status = await self .result_queue .get ()
29+ yield res , status
30+
31+ async def receive_results (self , socket ):
32+ last_message = ""
33+ async for message in socket :
34+ if message != "Done!" :
35+ if last_message != message :
36+ last_message = message
37+
38+ if last_message :
39+ decode_message = last_message .encode ().decode ('unicode_escape' )
40+ status = STATUS_OK if not app .need_exit () else STATUS_EXIT
41+ await self .result_queue .put ((json .loads (decode_message ), status ))
42+ print ("Received: %s" % decode_message )
43+ else :
44+ return last_message
45+
46+ async def record_audio (self ):
47+ while True :
48+ data = self .recorder .record (50 )
49+ status = STATUS_OK if not app .need_exit () else STATUS_EXIT
50+ await self .audio_queue .put ((data , status ))
51+ await asyncio .sleep (0.005 )
52+
53+ async def run (self , queues ):
54+ while not app .need_exit ():
55+ ip , port = await queues [0 ].get ()
56+ print (f'recv ip:{ ip } port:{ port } ' )
57+ server_url = f"ws://{ ip } :{ port } "
58+ try :
59+ async with websockets .connect (server_url ) as websocket :
60+ await queues [1 ].put ('OK' )
61+ self .receive_task = asyncio .create_task (self .receive_results (websocket ))
62+ self .audio_task = asyncio .create_task (self .record_audio ())
63+ print ("Started! Please Speak" )
64+ async for indata , status in self .read_audio_queue ():
65+ if status == STATUS_EXIT :
66+ return
67+ samples_int16 = np .frombuffer (indata , dtype = np .int16 )
68+ samples_float32 = samples_int16 .astype (np .float32 )
69+ samples_float32 = samples_float32 / 32768
70+ send_bytes = samples_float32 .tobytes ()
71+ await websocket .send (send_bytes )
72+ except websockets .exceptions .ConnectionClosedError as e :
73+ await queues [1 ].put ('SCAN AGAIN' )
74+ print (f'websockets exception { e } ' )
75+ except websockets .exceptions .WebSocketException as e :
76+ await queues [1 ].put ('SCAN AGAIN' )
77+ print (f'websockets exception { e } ' )
78+ except Exception as e :
79+ await queues [1 ].put ('SCAN AGAIN' )
80+ print (f'exception { e } ' )
81+
82+ async def ui (cam , disp , client , queues ):
83+ detector = image .QRCodeDetector ()
84+ status = 1 # 0, standby 1,scan qrcode 2,show the result of asr
85+ err_msg = ''
86+ ip = ''
87+ port = 0
88+ while not app .need_exit ():
89+ if status == 0 :
90+ msg = await queues [1 ].get ()
91+ if msg == 'OK' :
92+ status = 2
93+ elif msg == 'SCAN AGAIN' :
94+ status = 1
95+ err_msg = f'connect ws://{ ip } :{ port } failed!'
96+ elif status == 1 :
97+ img = cam .read ()
98+ qrcodes = detector .detect (img )
99+ if len (qrcodes ) > 0 :
100+ payload = qrcodes [0 ].payload ()
101+ try :
102+ json_res = json .loads (payload )
103+ ip = json_res ["ip" ]
104+ port = int (json_res ["port" ])
105+ print (f'send ip:{ ip } port:{ port } ' )
106+ img .clear ()
107+ img .draw_string (10 , 10 , f"connect to ws://{ ip } :{ port } .." )
108+ disp .show (img )
109+ await queues [0 ].put ((ip , port ))
110+ status = 0
111+ continue
112+ except Exception as e :
113+ print (f'Parse { payload } falied, except:{ e } ' )
114+ continue
115+ title = "Scan QRCode"
116+ msg1 = '1. Prepare a json string generated QR code.'
117+ msg2 = 'the format of json: {"ip":"127.0.0.1","port":6006}'
118+ msg3 = '2. Put the qrcode in the screen.'
119+ img .draw_string (10 , 10 , title )
120+ img .draw_string (10 , 40 , msg1 )
121+ img .draw_string (10 , 70 , msg2 )
122+ img .draw_string (10 , 100 , msg3 )
123+ img .draw_string (10 , 130 , err_msg , image .COLOR_RED )
124+ disp .show (img )
125+ else :
126+ base_img = image .Image (480 , 320 )
127+ base_img .draw_string (10 , 10 , "Please Speak..(Press the uesr key to exit)" , font = "sourcehansans" )
128+ disp .show (base_img )
129+ try :
130+ async for res , status in client .read_asr_result ():
131+ if status == STATUS_EXIT :
132+ return
133+ print (f'res:{ res } ' )
134+ img = base_img .copy ()
135+ img .draw_string (10 , 60 , res ['text' ], image .COLOR_WHITE , font = "sourcehansans" )
136+ disp .show (img )
137+ except :
138+ print ('something exit' )
139+ return
140+
141+
142+ async def main ():
143+ image .load_font ("sourcehansans" , "/maixapp/share/font/SourceHanSansCN-Regular.otf" )
144+ cam = camera .Camera (480 , 320 )
145+ disp = display .Display ()
146+ qrcode_queues = [asyncio .Queue (), asyncio .Queue ()]
147+ exit_event = asyncio .Event ()
148+ client = AsrOnlineClient (exit_event )
149+
150+ ui_task = asyncio .create_task (ui (cam , disp , client , qrcode_queues ))
151+ client_task = asyncio .create_task (client .run (qrcode_queues ))
152+ tasks = [ui_task , client_task ]
153+ try :
154+ await asyncio .gather (* tasks )
155+ except Exception as e :
156+ print (f'Exception caught: { e } ' )
157+
158+ if __name__ == '__main__' :
159+ asyncio .run (main ())
0 commit comments