1. 程式人生 > >微信小程式語音同步智慧識別的實現案例

微信小程式語音同步智慧識別的實現案例

[toc] #### 一、背景 在小程式的一些應用場景中,會有語音轉文字的需求。原有的做法一般是先通過小程式的錄音功能錄下語音檔案,然後再通過呼叫語音智慧識別WebApi(比如百度雲AI平臺,科大訊飛平臺)將語音檔案轉成文字資訊,以上的做法比較繁瑣且使用者的體驗性較差。 為解決此問題,**微信直接開放了同聲傳譯的外掛**,小程式作者可以直接使用該外掛進行語音同聲傳譯的開發。此文章將通過前後端整合應用的完整案例完成語音的實時轉換,並將語音上傳到服務端後臺備份。 #### 二、同聲傳譯外掛介紹 > 微信同聲傳譯由微信智聆語音團隊、微信翻譯團隊與公眾平臺聯合推出的同傳開放介面,首期開放、文字翻譯、語音合成介面,為開發者賦能。 ##### 1、 微信小程式後臺新增外掛 進入微信小程式後臺-->進入設定-->第三方設定-->新增外掛->搜尋同聲傳譯-->完成新增。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200424084858933.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70) ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200424084759730.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70) ##### 2、 微信小程式啟用外掛 在小程式app.json檔案中增加外掛版本等資訊: ```javascript "plugins": { "WechatSI": { "version": "0.3.3", "provider": "wx069ba97219f66d99" } }, ``` 在頁面程式檔案中引入外掛: ```javascript /* index.js */ const plugin = requirePlugin("WechatSI") // 獲取**全域性唯一**的語音識別管理器**recordRecoManager** const manager = plugin.getRecordRecognitionManager() ``` **recordRecoManager 物件的方法列表:** | 方法 |引數 | 說明 | |--|--|--| |start | options |開始識別 | |stop| |結束識別 | |onStart| callback|正常開始錄音識別時會呼叫此事件 | |onRecognize| callback|有新的識別內容返回,則會呼叫此事件 | |onStop| callback|識別結束事件 | |onError| callback|識別錯誤事件 | 官方開發文件:[外掛的語音識別管理器](https://mp.weixin.qq.com/wxopen/plugindevdoc?appid=wx069ba97219f66d99&token=&lang=zh_CN) #### 三、語音同步轉換的前端實現 ##### 1、介面UI與操作 UI參考微信官方的DEMO:長按按鈕進行錄音,鬆開按鈕實時將錄音轉換為文字。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/2020042409015977.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70) 使用者可對同步轉換的文字進行編輯,同時可將原始語音檔案與文字上傳後臺服務端。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200424091039788.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70) ##### 2、程式碼實現 語音同步轉換的主要程式碼: ```javascript //匯入外掛 const plugin = requirePlugin("WechatSI"); // 獲取**全域性唯一**的語音識別管理器**recordRecoManager** const manager = plugin.getRecordRecognitionManager(); /** * 載入進行初始化 */ onLoad: function () { //獲取錄音許可權 app.getRecordAuth(); //初始化語音識別回撥 this.initRecord(); }, ... /** * 初始化語音識別回撥 * 繫結語音播放開始事件 */ initRecord: function () { //有新的識別內容返回,則會呼叫此事件 manager.onRecognize = (res) => { let currentData = Object.assign({}, this.data.currentTranslate, { text: res.result, }); this.setData({ currentTranslate: currentData, }); this.scrollToNew(); }; // 識別結束事件 manager.onStop = (res) => { let text = res.result; console.log(res.tempFilePath); if (text == "") { this.showRecordEmptyTip(); return; } let lastId = this.data.lastId + 1; let currentData = Object.assign({}, this.data.currentTranslate, { text: res.result, translateText: "正在識別中", id: lastId, voicePath: res.tempFilePath, duration: res.duration }); this.setData({ currentTranslate: currentData, recordStatus: 1, lastId: lastId, }); //將當前識別內容與語音檔案加入列表 this.addRecordFile(currentData, this.data.dialogList.length); //重新整理列表 this.scrollToNew(); }; // 識別錯誤事件 manager.onError = (res) => { this.setData({ recording: false, bottomButtonDisabled: false, }); }; }, /** * 按住按鈕開始語音識別 */ streamRecord: function (e) { let detail = e.detail || {}; let buttonItem = detail.buttonItem || {}; //開始中文錄音 manager.start({ lang: buttonItem.lang, }); this.setData({ recordStatus: 0, recording: true, currentTranslate: { // 當前語音輸入內容 create: util.recordTime(new Date()), text: "正在聆聽中", lfrom: buttonItem.lang, lto: buttonItem.lto, }, }); //重新整理列表 this.scrollToNew(); }, /** * 鬆開按鈕結束語音識別 */ streamRecordEnd: function (e) { let detail = e.detail || {}; // 自定義元件觸發事件時提供的detail物件 let buttonItem = detail.buttonItem || {}; // 防止重複觸發stop函式 if (!this.data.recording || this.data.recordStatus != 0) { console.warn("has finished!"); return; } manager.stop(); this.setData({ bottomButtonDisabled: true, }); }, ``` 編輯識別文字並完上傳的主要程式碼: ```javascript /** * 頁面的初始資料 */ data: { edit_text_max: 200, remain_length: 200, edit_text: "", is_focus: false, tips: "", index: -1, voicePath: "", }, /** * 載入初始化 */ onLoad: function (options) { //根據傳入的文字內容填充編輯框 this.setEditText(options.content) this.setData({ index: index, oldText:options.content, voicePath: options.voicePath }) }, /** * 編輯文字 */ editInput: function (event) { console.log(event) if (event.detail.value.length > this.getEditTextMax()) { } else { this.data.edit_text = event.detail.value this.updateRemainLength(this.data.edit_text) } }, /** * 上傳文字與語音檔案 */ editConfirm: function (event) { let json=this.data.edit_text //呼叫微信上傳檔案api將資訊上傳至服務端webApi wx.uploadFile({ url: api.wxFileUploadUrl, filePath: this.data.voicePath, name: "file", header: { Authorization: wx.getStorageSync("loginFlag"), "Content-Type": "multipart/form-data", }, formData: { openId: app.globalData.userInfo.openId, realName: "語音檔案", json: JSON.stringify(json), }, success: (result) => { console.log("success:", result); if (result.statusCode == "200") { let data = JSON.parse(result.data); console.log("data", data); if (data.success == true) { let module = data.module; console.log("module", module); app.showInfo("上傳成功"); setTimeout( ()=>{ wx.navigateBack(); }, 2000) } else { app.showInfo("異常錯誤" + data.errMsg + ",請重新進入"); wx.navigateTo({ url: "/pages/index/index", }); } } else { app.showInfo("訪問後臺異常,重新進入系統"); wx.navigateTo({ url: "/pages/index/index", }); } }, fail: (result) => { console.log("fail", result); wx.navigateTo({ url: "/pages/index/index", }); }, complete: () => {}, }); }, ``` #### 四、後端SpringBoot實現語音檔案上傳webApi ##### 1、SpringBoot專案API相關結構樹 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200421145720337.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70) ##### 2、檔案上傳工具類的實現 tools工具類包中主要存檔案通用的檔案上傳工具類,該工具類會將檔案上傳至配置指定的資料夾下,並將檔案資訊寫入upload_file表中。 - 檔案資訊實體類:與資料庫中表upload_file對應; - 檔案儲存倉庫類:通過Spring Data JPA介面實現資料的CRUD; - 檔案上傳工具介面:對外統一封裝檔案上傳方法; - 檔案上傳工具實現類:實現檔案上傳方法介面。 檔案資訊實體類:UploadFile.java ```java /** * 檔案資訊表 * * @author zhuhuix * @date 2020-04-20 */ @Entity @Getter @Setter @Table(name = "upload_file") public class UploadFile { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @NotNull(groups = Update.class) private Long id; /** * 檔案實際名稱 */ @Column(name = "real_name") private String realName; /** * 檔名 */ @NotNull @Column(name = "file_name") private String fileName; /** * 檔案主名稱 */ @NotNull @Column(name = "primary_name") private String primaryName; /** * 副檔名 */ @NotNull private String extension; /** * 存放路徑 */ @NotNull private String path; /** * 檔案型別 */ private String type; /** * 檔案大小 */ private Long size; /** * 上傳人 */ private String uploader; @JsonIgnore @Column(name = "create_time") @CreationTimestamp private Timestamp createTime; public UploadFile(String realName, @NotNull String fileName, @NotNull String primaryName, @NotNull String extension, @NotNull String path, String type, Long size, String uploader) { this.realName = realName; this.fileName = fileName; this.primaryName = primaryName; this.extension = extension; this.path = path; this.type = type; this.size = size; this.uploader = uploader; } @Override public String toString() { return "UploadFile{" + "fileName='" + fileName + '\'' + ", uploader='" + uploader + '\'' + ", createTime=" + createTime + '}'; } } ``` 檔案儲存倉庫類:UploadFileRepository.java ```java /** * 上傳檔案DAO介面層 * * @author zhuhuix * @date 2020-04-03 */ public interface UploadFileRepository extends JpaRe