1. 程式人生 > >記一次智慧語音軟體的開發-終於為孩子找到了個靠譜的口語老師

記一次智慧語音軟體的開發-終於為孩子找到了個靠譜的口語老師

# 記一次智慧語音軟體的開發-終於為孩子找到了個靠譜的口語老師 “再窮不能窮教育,再苦不能苦孩子”,作為娃的爸媽,不僅僅要努力工作保證物質支援,更要關注娃的學習狀況,而且時刻都怕娃“輸在了起跑線上”,可是,現在孩子們的起跑線也太多了點,英語、各種藝術特長,甚至跳繩,忙的不亦樂乎。然而家長也不是全才啊,這不,我的姐姐最近就開始發愁女兒的英語口語問題了,自己發音不準確,報班又不知道哪家靠譜,眼看著孩子就要落後於小夥伴了,瞭解到這個情況後,我拿出英語課本,想到自己每次都是60飄過的英語成績,又放了回去,拿起了我的武器——程式碼。 ![](https://img2020.cnblogs.com/blog/1411116/202011/1411116-20201127162117199-1668762272.png) 近些年自然語言處理已經成熟地應用在很多領域,智慧語音測評的使用成本早已是大眾所能承受。根據給娃糾正讀音的需求,我最終選擇呼叫靠譜的大廠,有道智雲的[API](https://ai.youdao.com/DOCSIRMA/html/%E6%99%BA%E8%83%BD%E8%AF%AD%E9%9F%B3%E8%AF%84%E6%B5%8B/API%E6%96%87%E6%A1%A3/%E6%99%BA%E8%83%BD%E8%AF%AD%E9%9F%B3%E8%AF%84%E6%B5%8B%E6%9C%8D%E5%8A%A1/%E6%99%BA%E8%83%BD%E8%AF%AD%E9%9F%B3%E8%AF%84%E6%B5%8B%E6%9C%8D%E5%8A%A1-API%E6%96%87%E6%A1%A3.html)來開發個簡易[語音評測程式](https://github.com/LemonQH/BatchISEDemo),或者稱之為——智慧口語老師! ### 呼叫API介面的準備工作 首先,是需要在有道智雲的個人頁面上建立例項、建立應用、繫結應用和例項,獲取到應用的id和金鑰。具體個人註冊的過程和應用建立過程詳見文章[分享一次批量檔案翻譯的開發過程](https://www.cnblogs.com/alchemystar/p/13645324.html) ### 開發過程詳細介紹 下面介紹具體的程式碼開發過程。 首先研究[官方文件](https://ai.youdao.com/DOCSIRMA/html/%E6%99%BA%E8%83%BD%E8%AF%AD%E9%9F%B3%E8%AF%84%E6%B5%8B/API%E6%96%87%E6%A1%A3/%E6%99%BA%E8%83%BD%E8%AF%AD%E9%9F%B3%E8%AF%84%E6%B5%8B%E6%9C%8D%E5%8A%A1/%E6%99%BA%E8%83%BD%E8%AF%AD%E9%9F%B3%E8%AF%84%E6%B5%8B%E6%9C%8D%E5%8A%A1-API%E6%96%87%E6%A1%A3.html)給出的API輸入輸出規範。該API採用https方式通訊,簡單來說,就是將預先錄製好的聲音檔案編碼處理,簽名後提交給API,解析API返回的json即可得到評分結果。 介面地址: > https介面: [https://openapi.youdao.com/iseap](https://openapi.youdao.com/iseapi) API輸入所需引數如下表: | 欄位名 | 型別 | 含義 | 必填 | 備註 | | -------- | ---- | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ | | q | text | 要評測的音訊檔案的Base64編碼字串 | True | 必須是Base64編碼 | | text | text | 要評測的音訊檔案對應的文字 | True | have a good day | | langType | text | 源語言 | True | [支援語言](https://ai.youdao.com/DOCSIRMA/html/智慧語音評測/API文件/智慧語音評測服務/智慧語音評測服務-API文件.html#section-9) | | appKey | text | 應用ID | True | 可在 [應用管理](https://ai.youdao.com/appmgr.s) 檢視 | | salt | text | UUID | True | UUID | | curtime | text | 時間戳(秒) | True | TimeStamp | | sign | text | 簽名,通過sha256(應用ID+input+salt+curtime+應用金鑰)生成;input的生成規則見表下的備註 | True | sha256(應用ID+input+salt+curtime+應用金鑰) | | signType | text | 簽名型別 | True | v2 | | format | text | 語音檔案的格式,wav | true | wav | | rate | text | 取樣率,推薦 16000 採用率 | true | 16000 | | channel | text | 聲道數, 僅支援單聲道,請填寫固定值1 | true | 1 | | type | text | 上傳型別, 僅支援base64上傳,請填寫固定值1 | true | 1 | 其中籤名`sign`生成方法如下: signType=v2; sign=sha256(`應用ID`+`input`+`salt`+`curtime`+`應用金鑰`)。 這裡需要注意的是input的計算方式為:`input`=`q前10個字元` + `q長度` + `q後10個字元`(當q長度大於20)或 `input`=`q字串`(當q長度小於等於20)。 介面的輸出引數如下: | 欄位 | 含義 | | --------------- | ------------------------------------------------------------ | | errorCode | 識別結果錯誤碼,一定存在。 詳細資訊可見 [錯誤程式碼列表](https://ai.youdao.com/DOCSIRMA/html/智慧語音評測/API文件/智慧語音評測服務/智慧語音評測服務-API文件.html#section-10) | | refText | 請求的文字 | | start | 音訊中句子開始時間,單位是秒 | | end | 音訊中句子結束時間,單位是秒 | | integrity | 句子完整度得分 | | fluency | 句子流利度得分 | | pronunciation | 句子準確度得分 | | speed | 語速,單詞/分鐘 | | overall | 句子綜合評分 | | words | 單詞評分陣列 | | -word | 單詞 | | -start | 單詞開始時間,單位是秒 | | -end | 單詞結束時間,單位是秒 | | -pronunciation | 單詞準確度得分 | | -phonemes | 音標陣列 | | --phoneme | 音標 | | --start | 音標開始時間,單位是秒 | | --end | 音標結束時間,單位是秒 | | --judge | 判斷音素是否錯誤,true為發音正確,false為發音錯誤,同時calibration給出提示 | | --calibration | 如果發音錯誤,提示使用者該發音像什麼 | | --prominence | 重音程度,分數越高,當前音標越可能是重音,分數在[0 100] | | --stress_ref | 母音重音參考/標準答案,如果為true,說明參考答案認為該母音應該發重音,子音時無意義 | | --stress_detect | 在一個單詞中,使用者該音標發音為重音 | #### Demo開發: 這個demo使用python3開發,包括maindow.py,audioandprocess.py,isebynetease.py 三個檔案,分別為demo的介面、錄音以及其他邏輯處理和智慧語音評測介面呼叫方法的封裝。 1. ##### 介面部分: UI 部分大體分為三部分,文章處理區域、錄音區域和評分展示區域。
其佈局程式碼如下: ```python root=tk.Tk() root.title("youdao ise test") frm = tk.Frame(root) frm.grid(padx='50', pady='50') # 選取文章 btn_get_file_path=tk.Button(frm,text='選擇課文 :',command=get_file) btn_get_file_path.grid(row=0,column=0) text1=tk.Text(frm,width='70', height='2') text1.grid(row=0,column=1) # 文章內容展示 text2=tk.Text(frm,width='70', height='5') text2.grid(row=1,column=1) # 開始和停止錄音 btn_start_rec=tk.Button(frm,text='錄音',command=start_rec,width=10) btn_start_rec.grid(row=2,column=0) lb_Status = tk.Label(frm, text='Ready', anchor='w', fg='green') lb_Status.grid(row=2,column=1) btn_stop_rec=tk.Button(frm,text="結束錄音",command=stop_rec) btn_stop_rec.grid(row=2,column=2) # 打分按鈕和結果展示 btn_score=tk.Button(frm,text="評分",command=start_score,width=10) btn_score.grid(row=3,column=0) text3=tk.Text(frm,width='70', height='10') text3.grid(row=3,column=1) root.mainloop() ``` 其中啟動按鈕btn_score的繫結事件start_score()來收集帶所有的文字檔案,啟動合成,並列印執行結果: ```python def start_score(): result=au_model.get_score(file_dict) for r in result: text3.insert(tk.END,r) ``` 2. ##### audioandprocess.py 這裡主要實現了檔案處理、錄音和處理介面返回的功能。首先定義一個Audio_model ```python class Audio_model(): def __init__(self, audio_path,is_recording): self.current_file='' # 當前錄音對應的原文路徑 self.is_recording=is_recording # 錄音狀態標識 self.audio_chunk_size=1600 # 以下均為錄音必要引數 self.audio_channels=1 self.audio_format=pyaudio.paInt16 self.audio_rate=16000 ``` record_and_save()方法進行錄音並儲存到專案的record路徑中,錄音檔名與原文的檔名相同,便於對應。 ```python def record_and_save(self): self.is_recording = True file_name=self.get_file_name(self.current_file) self.audio_file_name='./record/'+file_name+'.wav' threading.Thread(target=self.record,args=(self.audio_file_name,)).start() ``` get_score()方法實現了呼叫isebynetease.py中封裝的工具並解析返回值的功能: ```python def get_score(self,dict): result=[] #self.is_recording=False for path in dict: file_content=self.get_content(path) file_name=self.get_file_name(path) audio_path='./record/'+file_name+'.wav' print(file_content,audio_path) score_result=connect(audio_path,file_content) #處理結果,新增進結果集 result.append( score_result) return result ``` 3. ##### isebynetease.py isebynetease.py中是和請求有道智雲API直接相關的一些方法,最核心的是connect()方法,整合了API所要求的各個引數,並呼叫執行請求的方法do_request(),而後根據UI的展示需求,處理API的返回結果並拼接字串。 ```python def connect(audio_file_path,audio_text): recordname=audio_file_path.split("/")[-1] audio_file_path = audio_file_path lang_type = 'en' # 當前僅支援英文 extension = audio_file_path[audio_file_path.rindex('.')+1:] if extension != 'wav': print('不支援的音訊型別') sys.exit(1) wav_info = wave.open(audio_file_path, 'rb') sample_rate = wav_info.getframerate() nchannels = wav_info.getnchannels() wav_info.close() with open(audio_file_path, 'rb') as file_wav: q = base64.b64encode(file_wav.read()).decode('utf-8') data = {} data['text'] = audio_text curtime = str(int(time.time())) data['curtime'] = curtime salt = str(uuid.uuid1()) signStr = APP_KEY + truncate(q) + salt + curtime + APP_SECRET sign = encrypt(signStr) data['appKey'] = APP_KEY data['q'] = q data['salt'] = salt data['sign'] = sign data['signType'] = "v2" data['langType'] = lang_type data['rate'] = sample_rate data['format'] = 'wav' data['channel'] = nchannels data['type'] = 1 # 處理返回值 response = do_request(data) j = json.loads(str(response.content, encoding="utf-8")) print(j) # 句子完整度 contextIntegrity="句子完整度:"+str( round(j["integrity"], 2))+" " pronunciation="發音準確度:"+str(round(j["pronunciation"],2))+" " fluency="流利度:"+str(round(j["fluency"],2))+" " speed="語速:" +str(round(j["speed"],2))+" " recordAndResult=recordname+" "+contextIntegrity+pronunciation+fluency+speed+"\n" return recordAndResult ``` ### 效果展示 展示一下本人純正的”chinenglish“ 錄音後程序的執行情況(得分多少不重要,重要的是它客觀的評價方式 :P ) ###### 首先介紹一下操作方法: 1)點選“選擇文章”,選擇需要評測的文章; 2)點選“錄音”,“結束錄音”按鈕,進行語音錄製; 3)如需對多篇文章進行評測,重複1)、2)步驟即可 4)點選“評分“,進行智慧語音評測,並展示評分結果,同時將詳細評分結果,儲存在本程式碼路徑的result目錄下。 ![](https://img2020.cnblogs.com/blog/1411116/202011/1411116-20201127162639278-2081719630.gif) ###### 效果展示 介面部分:展示了 句子完整度、發音準確度的、流利度的得分,以及語速: ![](https://img2020.cnblogs.com/blog/1411116/202011/1411116-20201127162707817-1232735708.png) 文件部分:分別對每個語音進行了測評,並將返回的詳細結果以json的形式存在了result資料夾下。 ![](https://img2020.cnblogs.com/blog/1411116/202011/1411116-20201127162729435-1609378239.png) 輸出結果展示: ``` { ’integrity‘: 100,//句子完整度 'refText’: "Are you ok? ",//待評測語音對應的文字 'pronunciation': 67.108101,//句子發音準確度 'start': 0.030000,//音訊開始時間,秒 'words': [{ //單詞資訊列表 'pronunciation': 50.640327, //單詞準確度分數 'start': 0.73, //單詞開始時間,秒 'end': 0.76,//單詞結束時間,秒 'word': 'Are', //單詞文字 'phonemes': [{ //音標資訊列表 'stress_ref': False, //母音重音參考(即標準重音),如果為true,說明參考答案認為該母音應該發重音,子音時無意義 'pronunciation': 50.640331, //音標準確度評分 'stress_detect': False,//在一個單詞中,使用者該音標發音不為重音 'phoneme': 'ɝ', //音標名稱 'start': 0.73, //音標開始時間,秒 'end': 0.76, //音標結束時間,秒 'judge': True, //判斷音標是否錯誤,true為發音正確,false為發音錯誤,同時calibration給出提示 'calibration': 'ɝ', //判斷音標是否錯誤,true為發音正確,false為發音錯誤,同時calibration給出提示 'prominence': 1 //重音程度,當前音標越可能是重音,分數區間[0 100] }] }, { 'pronunciation': 76.810608, 'start': 0.77, 'end': 1.08, 'word': 'you', 'phonemes': [{ 'stress_ref': False, 'pronunciation': 79.084282, 'stress_detect': False, 'phoneme': 'j', 'start': 0.77, 'end': 0.86, 'judge': True, 'calibration': 'j', 'prominence': 0.944885 }, { 'stress_ref': True, 'pronunciation': 74.536934, 'stress_detect': True, 'phoneme': 'u', 'start': 0.87, 'end': 1.08, 'judge': True, 'calibration': 'u', 'prominence': 1 }] }, { 'pronunciation': 66.129013, 'start': 1.14, 'end': 1.8, 'word': 'ok', 'phonemes': [{ 'stress_ref': True, 'pronunciation': 69.046341, 'stress_detect': True, 'phoneme': 'o', 'start': 1.14, 'end': 1.27, 'judge': True, 'calibration': 'o', 'prominence': 1 }, { 'stress_ref': False, 'pronunciation': 65.357841, 'stress_detect': False, 'phoneme': 'k', 'start': 1.28, 'end': 1.42, 'judge': True, 'calibration': 'k', 'prominence': 0.838557 }, { 'stress_ref': True, 'pronunciation': 63.982838, 'stress_detect': True, 'phoneme': 'e', 'start': 1.43, 'end': 1.8, 'judge': True, 'calibration': 'e', 'prominence': 0.956448 }] }], 'fluency': 83.554047, //句子流利度 'overall': 83.885124,//句子綜合評分 'errorCode': '0', //識別結果錯誤碼,一定存在 'end': 1.8,//句子結束時間,秒 'speed': 55.555557 // 句子語速(單詞/分鐘) } ``` ### 總結 有道智雲的智慧語音評測API文件清晰,呼叫過程全程無坑,開發體驗非常友好,評分結果客觀公正,很具有參考價值,以至於我都想和小侄女一起學習進步去了! 專案地址:https://github.com/LemonQH/BatchISEDemo