私信小编001即可获取大量Python学习资料
最近,某水果手机厂在万众期待中开了一场没有发布万众期待的手机产品的发布会,发布了除手机外的其他一些产品,也包括最新的水果14系统。几天后,更新了系统的吃瓜群众经过把玩突然发现新系统里一个超有意思的功能——翻译
奇怪的翻译知识增加了!
相比常见的翻译工具,同声翻译工具更具有实用价值,想想不精通其他语言就能和歪果朋友无障碍交流的场景,真是一件美事,不如自己动手实现个工具备用!一个同声翻译工具,逻辑大概可以是先识别,而后翻译,翻译能否成功,识别的准确率是个关键因素。为了降低难度,我决定分两次完成工具开发。首先来实现试试语音识别的部分。
轻车熟路,本次的demo继续调用有道智云API,实现实时语音识别。
先看看界面和结果哈:
可以选择多种语音,这里只写了四种常见的:
我分别测试的中文、韩文、英文。看着还不错哦~
这里翻译结果,是根据音频一个字、一个字这样实时识别的,由于识别速度比较快,所以看起来木有时间差。
首先,是需要在有道智云的个人页面上创建实例、创建应用、绑定应用和实例,获取调用接口用到的应用的id和密钥。具体个人注册的过程和应用创建过程详见文章分享一次批量文件翻译的开发过程
下面介绍具体的代码开发过程。
首先是根据实时语音识别文档来分析接口的输入输出。接口设计的目的是对连续音频流的实时识别,转换成文本信息并返对应文字流,因此通信采用websocket,调用过程分为认证、实时通信两阶段。
在认证阶段,需发送以下参数:
参数类型必填说明示例appKeyString是已申请的应用IDIDsaltString是UUIDUUIDcurtimeString是时间戳(秒)TimeStampsignString是加密数字签名。sha256signTypeString是数字签名类型v4langTypeString是语言选择,参考支持语言列表zh-CHSformatString是音频格式,支持wavwavchannelString是声道,支持1(单声道)1versionString是api版本v1rateString是采样率16000
签名sign生成方法如下:
signType=v4;
sign=sha256(应用ID+salt+curtime+应用密钥)。
认证之后,就进入了实时通信阶段,发送音频流,获取识别结果,最后发送结束标志结束通信,这里需要注意的是,发送的音频最好是16bit位深的单声道、16k采样率的清晰的wav音频文件,这里我开发时最开始因为音频录制设备有问题,导致音频效果极差,接口一直返回错误码304(手动捂脸)。
这个demo使用python3开发,包括maindow.py,audioandprocess.py,recobynetease.py三个文件。界面部分,使用python自带的tkinter库,来进行语言选择、录音开始、录音停止并识别的操作。audioandprocess.py实现了录音、音频处理的逻辑,最后通过recobynetease.py中的方法来调用实时语音识别API。
主要元素:
root&#61;tk.Tk()root.title("netease youdao translation test")frm &#61; tk.Frame(root)frm.grid(padx&#61;&#39;80&#39;, pady&#61;&#39;80&#39;)label&#61;tk.Label(frm,text&#61;&#39;选择语言类型&#xff1a;&#39;)label.grid(row&#61;0,column&#61;0)combox&#61;ttk.Combobox(frm,textvariable&#61;tk.StringVar(),width&#61;38)combox["value"]&#61;lang_type_dictcombox.current(0)combox.bind("<>",get_lang_type)combox.grid(row&#61;0,column&#61;1)btn_start_rec &#61; tk.Button(frm, text&#61;&#39;开始录音&#39;, command&#61;start_rec)btn_start_rec.grid(row&#61;2, column&#61;0)lb_Status &#61; tk.Label(frm, text&#61;&#39;Ready&#39;, anchor&#61;&#39;w&#39;, fg&#61;&#39;green&#39;)lb_Status.grid(row&#61;2,column&#61;1)btn_sure&#61;tk.Button(frm,text&#61;"结束并识别",command&#61;get_result)btn_sure.grid(row&#61;3,column&#61;0)root.mainloop()
选择语言类型之后&#xff0c;开始录音&#xff0c;录音结束后&#xff0c;通过get_result()方法调用接口进行识别。
def get_result(): lb_Status[&#39;text&#39;]&#61;&#39;Ready&#39; sr_result&#61;au_model.stop_and_recognise()
音频录制部分引入pyaudio库(需通过pip安装)来调用音频设备并录制接口要求的wav文件&#xff0c;并调用wave库存储音频文件。
Audio_model类的构造&#xff1a;
def __init__(self, audio_path, language_type,is_recording): self.audio_path &#61; audio_path,# 录音存储路径 self.audio_file_name&#61;&#39;&#39;# 录音文件名 self.language_type &#61; language_type,# 录音语言类型 self.language_dict&#61;["zh-CHS","en","ja","ko"]# 支持的语言&#xff0c;用于从UI出的类型转为接口所需类型 self.language&#61;&#39;&#39; self.is_recording&#61;is_recording# 录音状态 self.audio_chunk_size&#61;1600# 以下为一些接口所要求的录音参数&#xff0c;采样率、编码、通道等 self.audio_channels&#61;1 self.audio_format&#61;pyaudio.paInt16 self.audio_rate&#61;16000
record()方法中实现了录音的逻辑&#xff0c;调用pyaudio库&#xff0c;读取音频流&#xff0c;写入文件。
def record(self,file_name): p&#61;pyaudio.PyAudio() stream&#61;p.open( format&#61;self.audio_format, channels&#61;self.audio_channels, rate&#61;self.audio_rate, input&#61;True, frames_per_buffer&#61;self.audio_chunk_size ) wf &#61; wave.open(file_name, &#39;wb&#39;) wf.setnchannels(self.audio_channels) wf.setsampwidth(p.get_sample_size(self.audio_format)) wf.setframerate(self.audio_rate) # 读取数据写入文件 while self.is_recording: data &#61; stream.read(self.audio_chunk_size) wf.writeframes(data) wf.close() stream.stop_stream() stream.close() p.terminate()
stop_and_recognise()方法将Audio_model的录音状态标记为false&#xff0c;并启动调用有道智云API的方法。
def stop_and_recognise(self): self.is_recording&#61;False recognise(self.audio_file_name,self.language_dict[self.language_type])
有道智云实时语音识别接口使用socket通信&#xff0c;为简化展示逻辑&#xff0c;因此在此处发开了展示识别结果的界面&#xff0c;使用tkinter显示&#xff1a;
#输出结果的窗口root &#61; tk.Tk()root.title("result")frm &#61; tk.Frame(root)frm.grid(padx&#61;&#39;80&#39;, pady&#61;&#39;80&#39;)text_result &#61; tk.Text(frm, width&#61;&#39;40&#39;, height&#61;&#39;20&#39;)text_result.grid(row&#61;0, column&#61;1)
recognise()方法根据接口文档&#xff0c;将所需参数拼接到uri&#xff0c;传给start()方法请求接口&#xff1a;
def recognise(filepath,language_type): print(&#39;l:&#39;&#43;language_type) global file_path file_path&#61;filepath nonce &#61; str(uuid.uuid1()) curtime &#61; str(int(time.time())) signStr &#61; app_key &#43; nonce &#43; curtime &#43; app_secret print(signStr) sign &#61; encrypt(signStr) uri &#61; "wss://openapi.youdao.com/stream_asropenapi?appKey&#61;" &#43; app_key &#43; "&s&curtime&#61;" &#43; curtime &#43; "&sign&#61;" &#43; sign &#43; "&version&#61;v1&channel&#61;1&format&#61;wav&signType&#61;v4&rate&#61;16000&langType&#61;" &#43; language_type print(uri) start(uri, 1600)
start()方法是实时识别部分的核心方法&#xff0c;通过websocket调用识别接口。
def start(uri): websocket.enableTrace(True) ws &#61; websocket.WebSocketApp(uri, on_message&#61;on_message, on_error&#61;on_error, on_close&#61;on_close) ws.on_open &#61; on_opend ws.run_forever()
在请求接口时&#xff0c;首先读取先前录制的音频文件&#xff0c;并发送&#xff1a;
def on_open(ws): count &#61; 0 file_object &#61; open(file_path, &#39;rb&#39;) #打开录制的音频 while True: chunk_data &#61; file_object.read(1600) ws.send(chunk_data, websocket.ABNF.OPCODE_BINARY) #发送 time.sleep(0.05) count &#61; count &#43; 1 if not chunk_data: break print(count) ws.send(&#39;{"end": "true"}&#39;, websocket.ABNF.OPCODE_BINARY)
而后在通信过程中处理接口返回的消息&#xff0c;收集接口返回的识别结果&#xff1a;
def on_message(ws, message): result&#61;json.loads(message) resultmessage&#61; result[&#39;result&#39;] #解析调用接口的返回结果 if resultmessage: resultmessage1 &#61; result[&#39;result&#39;][0] resultmessage2 &#61; resultmessage1["st"][&#39;sentence&#39;] print(resultmessage2) #text_result.insert(tk.END, resultmessage2&#43;&#39;&#39;) result_arr.append(resultmessage2)
最后在通信结束后展示识别结果&#xff1a;
def on_close(ws): print_resule(result_arr) print("### closed ###") def print_resule(arr): text_result.delete(&#39;1.0&#39;,tk.END) for n in arr: text_result.insert("insert", n &#43; &#39;&#39;)
有道智云提供的接口一如既往的好用&#xff0c;这次开发主要的精力全都浪费在了由于我自己录制的音频质量差而识别失败的问题上&#xff0c;音频质量ok后&#xff0c;识别结果准确无误&#xff0c;下一步就是拿去翻译了&#xff0c;有了有道智云API&#xff0c;实现实时翻译也可以如此简单