Open klren0312 opened 1 year ago
import { Base64 } from './base64' import CryptoJS from 'crypto-js' const app = getApp() const APPID = app.globalData.APPID const API_KEY = app.globalData.API_KEY const API_SECRET = app.globalData.API_SECRET export function getWebSocketUrl() { return new Promise((resolve, reject) => { // 请求地址根据语种不同变化 var url = 'wss://iat-api.xfyun.cn/v2/iat' var host = 'iat-api.xfyun.cn' var apiKey = API_KEY var apiSecret = API_SECRET var date = new Date().toGMTString() var algorithm = 'hmac-sha256' var headers = 'host date request-line' var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/iat HTTP/1.1` var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret) var signature = CryptoJS.enc.Base64.stringify(signatureSha) var authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"` var authorization = Base64.encode(authorizationOrigin) const params = encodeURI(`authorization=${authorization}&date=${date}&host=${host}`) url = `${url}?${params}` resolve(url) }) } export class IatRecorder { constructor({ language, accent, appId } = {}) { this.firstSend = true this.status = 'null' this.language = language || 'zh_cn' this.accent = accent || 'mandarin' this.appId = appId || APPID // 记录音频数据 this.audioData = [] // 记录听写结果 this.resultText = '' // wpgs下的听写结果需要中间状态辅助记录 this.resultTextTemp = '' this.options = { duration: 60000, // 指定录音的时常,单位ms sampleRate: 16000, // 采样率 numberOfChannels: 1, // 录音通道数 format: 'PCM', // 音频格式 frameSize: 5, // 指定帧大小,单位KB } } // 修改录音听写状态 setStatus(status) { this.onWillStatusChange && this.status !== status && this.onWillStatusChange(this.status, status) this.status = status } setResultText({ resultText, resultTextTemp } = {}) { this.onTextChange && this.onTextChange(resultTextTemp || resultText || '') resultText !== undefined && (this.resultText = resultText) resultTextTemp !== undefined && (this.resultTextTemp = resultTextTemp) } // 修改听写参数 setParams({ language, accent } = {}) { language && (this.language = language) accent && (this.accent = accent) } // 连接websocket connectWebSocket() { getWebSocketUrl().then(url => { this.webSocket = wx.connectSocket({ url, success: e => { wx.showToast({ title: '请说话', }) this.recordManager.start(this.options) }, fail: e => { console.error(e) } }) console.log(this.webSocket) this.setStatus('init') this.webSocket.onOpen(e => { console.log('open') this.setStatus('ing') // 重新开始录音 setTimeout(() => { // this.webSocketSend() }, 500) }) this.webSocket.onMessage(e => { this.result(e.data) }) this.webSocket.onError(e => { console.error(e) this.recorderStop() }) this.webSocket.onClose(e => { this.recorderStop() }) }) } // 初始化浏览器录音 recorderInit() { this.recordManager = wx.getRecorderManager() console.log(this.recordManager) this.recordManager.onStart(() => { console.log('recorder start') }) this.recordManager.onPause(() => { console.log('recorder pause') }) this.recordManager.onStop((res) => { // tempFilePath String 录音文件的临时路径 console.log('recorder stop', res) }) this.recordManager.onError((err) => { // errMsg String 错误信息 console.log('recorder err', err) }) this.recordManager.onFrameRecorded((res) => { const { frameBuffer, isLastFrame } = res const int16Arr = new Int8Array(frameBuffer) let params = {} if (this.firstSend) { this.firstSend = false params = { common: { app_id: this.appId, }, business: { language: this.language, //小语种可在控制台--语音听写(流式)--方言/语种处添加试用 domain: 'iat', accent: this.accent, //中文方言可在控制台--语音听写(流式)--方言/语种处添加试用 vad_eos: 5000, dwa: 'wpgs', //为使该功能生效,需到控制台开通动态修正功能(该功能免费) }, data: { status: 0, format: 'audio/L16;rate=16000', encoding: 'raw', audio: wx.arrayBufferToBase64(int16Arr), }, } } else { if (isLastFrame) { params = { data: { status: 2 } } } else { params = { data: { status: 1, format: 'audio/L16;rate=16000', encoding: 'raw', audio: wx.arrayBufferToBase64(int16Arr), } } } } this.webSocket && this.webSocket.send({ data: JSON.stringify(params) }) }) this.connectWebSocket() } recorderStart() { this.recorderInit() } // 停止录音 recorderStop() { if (this.recordManager) { this.recordManager.stop() this.recordManager = null } this.webSocket && this.webSocket.close() this.webSocket = null this.audioData = [] this.handlerInterval && clearInterval(this.handlerInterval) this.handlerInterval = null this.firstSend = true } // 向webSocket发送数据 webSocketSend() { if (!this.webSocket) { return } let audioData = this.audioData.splice(0, 1280) console.log('开始发送', audioData) var params = { common: { app_id: this.appId, }, business: { language: this.language, //小语种可在控制台--语音听写(流式)--方言/语种处添加试用 domain: 'iat', accent: this.accent, //中文方言可在控制台--语音听写(流式)--方言/语种处添加试用 vad_eos: 5000, dwa: 'wpgs', //为使该功能生效,需到控制台开通动态修正功能(该功能免费) }, data: { status: 0, format: 'audio/L16;rate=16000', encoding: 'raw', audio: Base64.encode(audioData), }, } this.webSocket.send({ data: JSON.stringify(params) }) this.handlerInterval = setInterval(() => { // websocket未连接 if (!this.webSocket) { this.audioData = [] clearInterval(this.handlerInterval) return } if (this.audioData.length === 0) { if (this.status === 'end') { this.webSocket.send({ data: JSON.stringify({ data: { status: 2, format: 'audio/L16;rate=16000', encoding: 'raw', audio: '', }, }) }) this.audioData = [] clearInterval(this.handlerInterval) } return false } audioData = this.audioData.splice(0, 1280) console.log('开始发送', audioData) // 中间帧 this.webSocket.send({ data: JSON.stringify({ data: { status: 1, format: 'audio/L16;rate=16000', encoding: 'raw', audio: Base64.encode(audioData), }, }) }) }, 40) } result(resultData) { console.log(resultData) // 识别结束 let jsonData = JSON.parse(resultData) if (jsonData.data && jsonData.data.result) { let data = jsonData.data.result let str = '' let resultStr = '' let ws = data.ws for (let i = 0; i < ws.length; i++) { str = str + ws[i].cw[0].w } // 开启wpgs会有此字段(前提:在控制台开通动态修正功能) // 取值为 "apd"时表示该片结果是追加到前面的最终结果;取值为"rpl" 时表示替换前面的部分结果,替换范围为rg字段 if (data.pgs) { if (data.pgs === 'apd') { // 将resultTextTemp同步给resultText this.setResultText({ resultText: this.resultTextTemp, }) } // 将结果存储在resultTextTemp中 this.setResultText({ resultTextTemp: this.resultText + str, }) } else { this.setResultText({ resultText: this.resultText + str, }) } } if (jsonData.code === 0 && jsonData.data.status === 2) { this.webSocket.close() this.webSocket = null } if (jsonData.code !== 0) { this.webSocket.close() this.webSocket = null console.log(`${jsonData.code}:${jsonData.message}`) } } start() { this.recorderStart() this.setResultText({ resultText: '', resultTextTemp: '' }) } stop() { this.webSocket && this.webSocket.send({ data: JSON.stringify({ data: { status: 2, }, }) }) this.recorderStop() } }
👍