vue + 科大讯飞语音听写功能(解决vue new Worker报错问题)
1.要求:做语音,然后转成文字
2.使用api:科大讯飞语音听写
3.在 vue 项目中配置文件
4.配置后处理iatrecorder JS中new Woeker()会报错的问题
现在:
第一步:下载一个js版本的demo,获取iatrecorder js和转码worker。复制。 js文件到src

转码.worker.js:
// (函数(){
self.onmessage u003d 函数(e){
transAudioData.transcode(e.data)
}
让 transAudioData u003d {
转码(音频数据){
让输出 u003d transAudioData.to16kHz(audioData)
输出 u003d transAudioData.to16BitPCM(输出)
输出 u003d Array.from(new Uint8Array(output.buffer))
self.postMessage(输出)
// 返回输出
},
to16kHz(音频数据){
var 数据 u003d 新 Float32Array(audioData)
var fitCount u003d Math.round(data.length * (16000 / 44100))
var newData u003d new Float32Array(fitCount)
var springFactor u003d (data.length - 1) / (fitCount - 1)
新数据[0] u003d 数据[0]
for (让 i u003d 1; i < fitCount - 1; i++) {
var tmp u003d i * springFactor
var before u003d Math.floor(tmp).toFixed()
var after u003d Math.ceil(tmp).toFixed()
var atPoint u003d tmp - 之前
newData[i] u003d data[before] + (data[after] - data[before]) * atPoint
}
newData[fitCount - 1] u003d 数据[data.length - 1]
返回新数据
},
至 16 位 PCM(输入){
var dataLength u003d input.length * (16 / 8)
var dataBuffer u003d new ArrayBuffer(dataLength)
var 数据视图 u003d 新数据视图(数据缓冲区)
其中偏移量 u003d 0
for (var i u003d 0; i < input.length; i++, offset +u003d 2) {
var s u003d Math.max(-1, Math.min(1, input[i]))
dataView.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true)
}
返回数据视图
},
}
// })()
IatRecorder.js:
这里需要手动安装crypto JS,然后从'crypto JS'导入cryptojs
npm install crypto-js --save-dev
const APPID u003d '你的 APPID'
const API_SECRET u003d '你的 API_SECRET '
const API_KEY u003d '你的 API_KEY'
从“crypto-js”导入 CryptoJS
从 './transcode.worker.js' 导入 Worker
常量 transWorker u003d new Worker()
控制台.log(transWorker)
变量开始时间 u003d ""
变量结束时间 u003d ""
函数 getWebSocketUrl(){
返回新的承诺((解决,拒绝)u003d> {
// 请求地址根据不同语言变化
var url u003d 'wss://iat-api.xfyun.cn/v2/iat'
var 主机 u003d 'iat-api.xfyun.cn'
var apk u003d apk_key
var apiSecret u003d API_SECRET
var date u003d new Date().toGMTString()
远算法 u003d 最重要的شئ
var headers u003d '主机日期请求行'
变体签名来源 u003d `host: ${host}\ndate: ${date}\nGET /v2/iat HTTP/1.1`
var signatureSha u003d CryptoJS.HmacSHA256(signatureOrigin, apiSecret)
var 签名 u003d CryptoJS.enc.Base64.stringify(signatureSha)
var 授权来源 u003d `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`
var 授权 u003d btoa(authorizationOrigin)
网址 u003d `${url}?authorization=${authorization}&date=${date}&host=${host}`
解决(网址)
})
}
常量 IatRecorder u003d 类 {
构造函数({ 语言、口音、appId } u003d {}){
让自我 u003d 这个
this.status u003d 'null'
this.language u003d 语言 || 'zh_cn'
this.accent u003d 重音 || '普通话'
this.appId u003d appId || APPID
// 录制音频数据
this.audioData u003d []
// 记录听写结果
this.resultText u003d ''
// wpgs下的听写结果需要记录在中间状态
this.resultTextTemp u003d ''
transWorker.onmessage u003d 函数(事件){
// console.log("在构造方法中", self.audioData)
self.audioData.push(...event.data)
}
}
// 修改录音听写状态
设置状态(状态){
this.onWillStatusChange && this.status !u003du003d 状态 && this.onWillStatusChange(this.status, status)
this.status u003d 状态
}
setResultText({ resultText, resultTextTemp } u003d {}) {
this.onTextChange && this.onTextChange(resultTextTemp || resultText || '')
resultText !u003du003d 未定义 && (this.resultText u003d resultText)
resultTextTemp !u003du003d 未定义 && (this.resultTextTemp u003d resultTextTemp)
}
// 修改听写参数
setParams({ 语言,口音 } u003d {}) {
语言 && (this.language u003d 语言)
重音 && (this.accent u003d 重音)
}
// 连接到 websocket
连接WebSocket() {
返回 getWebSocketUrl().then(url u003d> {
让 iatWS
if ('WebSocket' in window) {
iatWS u003d 新的 WebSocket(网址)
} else if ('MozWebSocket' in window) {
iatWS u003d 新 MozWebSocket(url)
} 其他 {
alert('浏览器不支持 WebSocket')
返回
}
this.webSocket u003d iatWS
this.setStatus('init')
so.onetime u003d h u003d> {
this.setStatus('ing')
// 重新开始录制
setTimeout(() u003d> {
this.webSocketSend()
}, 500)
}
iatWS.onmessage u003d e u003d> {
this.result(e.data)
}
iatWS.onerror u003d e u003d> {
this.recorderStop()
}
iaTOS.onclose u003d h u003d> {
endTime u003d Date.parse(新日期())
console.log("持续时间",endTime-startTime)
this.recorderStop()
}
})
}
// 初始化浏览器录制
记录器初始化() {
navigator.getUserMedia u003d
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia
// 创建音频环境
试试 {
this.audioContext u003d new (window.AudioContext || window.webkitAudioContext)()
this.audioContext.resume()
if (!this.audioContext) {
alert('浏览器不支持 webAudioApi 相关接口')
返回
}
} 捕捉 (e) {
if (!this.audioContext) {
alert('浏览器不支持 webAudioApi 相关接口')
返回
}
}
// 获取浏览器录制权限
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices
.getUserMedia({
音频:真实,
视频:错误,
})
.then(流 u003d> {
获取媒体成功(流)
})
.catch(e u003d> {
getMediaFail(e)
})
} else if (navigator.getUserMedia) {
navigator.getUserMedia(
{
音频:真实,
视频:错误,
},
流 u003d> {
获取媒体成功(流)
},
功能(e){
getMediaFail(e)
}
)
} 其他 {
if (navigator.userAgent.toLowerCase().match(/chrome/) && location.origin.indexOf('https://') < 0) {
alert('chrome要获取浏览器录音功能,因为安全问题,需要在localhost或者127.0.0.1或者https权限下才能获取')
} 其他 {
alert('无法获取浏览器的录音功能,请升级浏览器或使用chrome')
}
this.audioContext && this.audioContext.close()
返回
}
// 获取浏览器录制权限成功回调
让 getMediaSuccess u003d 流 u003d> {
// 创建一个直接通过 JavaScript 处理音频
this.scriptProcessor u003d this.audioContext.createScriptProcessor(0, 1, 1)
this.scriptProcessor.onaudioprocess u003d e u003d> {
// 处理音频数据
if (this.status u003du003du003d 'ing') {
transWorker.postMessage(e.inputBuffer.getChannelData(0))
// this.audioData.push(e.inputBuffer.getChannelData(0))
}
}
// 创建一个新的 mediastreamcaudiosourcenode 对象,以便可以播放和操作来自 MediaStream 的音频
this.mediaSource u003d this.audioContext.createMediaStreamSource(流)
// 连接
this.mediaSource.connect(this.scriptProcessor)
this.scriptProcessor.connect(this.audioContext.destination)
this.connectWebSocket()
}
让 getMediaFail u003d (e) u003d> {
this.audioContext && this.audioContext.close()
this.audioContext u003d 未定义
// 关闭 websocket
if (this.webSocket && this.webSocket.readyState u003du003du003d 1) {
this.webSocket.close()
}
}
}
记录器开始() {
if (!this.audioContext) {
this.recorderInit()
} 其他 {
this.audioContext.resume()
this.connectWebSocket()
}
}
// 暂停录制
记录器停止() {
// 在 safari 中挂起后再次恢复。录制内容将为空白。在 Safari 中设置不暂停
if (!(/Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgen))){
this.audioContext && this.audioContext.suspend()
}
this.setStatus('end')
}
// 处理音频数据
// transAudioData(audioData) {
// audioData u003d transAudioData.transaction(audioData)
// this.audioData.push(...audioData)
// }
// base64编码处理后的音频数据,
toBase64(缓冲区){
var 二进制 u003d ''
var bytes u003d new Uint8Array(buffer)
var len u003d bytes.byteLength
for (var i u003d 0; i < len; i++) {
二进制 +u003d String.fromCharCode(bytes[i])
}
返回 window.btoa(二进制)
}
// 发送数据到 webSocket
webSocketSend() {
if (this.webSocket.readyState !u003du003d 1) {
返回
}
让 audioData u003d this.audioData.splice(0, 1280)
变量参数 u003d {
常见:{
应用_id:this.appId,
},
业务:{
language: this.language, //控制台可以添加小语言-语音听写(流式)-方言/语言
域:'iat',
口音: this.accent, //可以在控制台添加汉语方言-语音听写(流式)-方言/语言
},
数据:{
状态:0,
格式:'audio/L16;rateu003d16000',
编码:'原始',
音频:this.toBase64(audioData),
},
}
console.log("参数语言:",this.language)
console.log("参数重音:",this.accent)
this.webSocket.send(JSON.stringify(params))
startTime u003d Date.parse(新日期())
this.handlerInterval u003d setInterval(() u003d> {
// websocket 未连接
if (this.webSocket.readyState !u003du003d 1) {
console.log("websocket 未连接")
this.audioData u003d []
clearInterval(this.handlerInterval)
返回
}
if (this.audioData.length u003du003du003d 0) {
console.log("自动关闭",this.status)
if (this.status u003du003du003d 'end') {
this.webSocket.send(
JSON.stringify({
数据:{
状态:2,
格式:'audio/L16;rateu003d16000',
编码:'原始',
音频:'',
},
})
)
this.audioData u003d []
clearInterval(this.handlerInterval)
}
返回假
}
audioData u003d this.audioData.splice(0, 1280)
// 中间帧
this.webSocket.send(
JSON.stringify({
数据:{
状态:1,
格式:'audio/L16;rateu003d16000',
编码:'原始',
音频:this.toBase64(audioData),
},
})
)
}, 40)
}
结果(结果数据){
// 识别结束
让 jsonData u003d JSON.parse(resultData)
if (jsonData.data && jsonData.data.result) {
让数据 u003d jsonData.data.result
让 str u003d ''
让 resultStr u003d ''
让 ws u003d data.ws
for (让 i u003d 0; i < ws.length; i++) {
str u003d str + ws[i].cw[0].w
}
console.log("识别结果为:",str)
//开启wpgs时该字段可用(前提:控制台开启动态修正功能)
// 当值为“apd”时,表示切片结果是追加到最前面的最终结果;当值为“rpl”时,表示替换之前的结果,替换范围为rg字段
如果(数据.pgs){
if (data.pgs u003du003du003d 'apd') {
// 将 resultTextTemp 同步到 resultText
this.setResultText({
结果文本:this.resultTextTemp,
})
}
// 将结果存储在 resultTextTemp 中
this.setResultText({
resultTextTemp:this.resultText + str,
})
} 其他 {
this.setResultText({
结果文本:this.resultText + str,
})
}
}
if (jsonData.code u003du003du003d 0 && jsonData.data.status u003du003du003d 2) {
this.webSocket.close()
}
if (jsonData.code !u003du003d 0) {
this.webSocket.close()
控制台.log(`${jsonData.code}:${jsonData.message}`)
}
}
开始() {
this.recorderStart()
this.setResultText({ resultText: '', resultTextTemp: '' })
}
停止() {
this.recorderStop()
}
}
导出默认 IatRecorder
第二步:配置你使用的页面:
<模板>
<div classu003d"conter">
<button @clicku003d"translationStart">开始</button>
<button @clicku003d"translationEnd">停止</button>
</div>
</模板>
<脚本>
从“@/assets/js/IatRecorder”导入 IatRecorder;
const iatRecorder u003d new IatRecorder('en_us','mandarin','9abbbfb0')//小语种-汉语方言-appId
导出默认 {
数据() {
返回{
};
},
方法:{
翻译开始() {
iatRecorder.start();
},
翻译结束() {
iatRecorder.onTextChange u003d (文本) u003d> {
让 inputText u003d 文本;
this.searchData u003d inputText.substring(0, inputText.length - 1); //文字处理,因为不知道为什么识别输出后面跟着'',这个方法去掉字符串的最后一位
控制台.log(this.searchData);
};
iatRecorder.stop();
},
}
};
</脚本>
引用的时候,问题来了,转码工。 JS文件

const transWorker u003d new Worker() 会报错因为原生new Worker不能直接在vue中使用
众所周知,JavaScript是单线程的,一些复杂耗时的操作会阻塞页面的渲染交互,造成页面卡顿,影响用户体验。 web worker 是 html5 的新特性之一。主要用于解决此类问题,为页面多开一个线程,在不影响主线程的情况下处理一些耗时的操作。
在实际vue项目的开发和使用过程中,遇到了很多问题
不同模块之间的通信主要是通过postMessage推送,通过onmessage接收,所以vue项目不能直接使用,必须配置。但是科大讯飞语音官网没有给出vue项目的例子,也没有特别说明,所以我也踩了个坑
第三步:解决new Worker()报错问题
首先,两个js文件都不需要更改
然后安装
npm install worker-loader -D
vue.config.js 添加以下配置:
configureWebpack: 配置 u003d> {
config.module.rules.push({
测试:/\.worker.js$/,
使用:{
装载机:“工人装载机”,
选项:{ 内联:true,名称:'workerName.[hash].js'}
}
})
},
运行的时候会发现控制台会报错,“window is undefined”。这是因为工作线程中没有window对象,所以不能直接使用。相反,在 Vue 配置中使用它。 JS
链Webpack:配置u003d> {
config.output.globalObject('this')
}
打包时添加:
平行:假
在一起是
模块.exports u003d {
configureWebpack: 配置 u003d> {
config.module.rules.push({
测试:/\.worker.js$/,
使用:{
装载机:“工人装载机”,
选项:{ 内联:true,名称:'workerName.[hash].js'}
}
})
},
平行:假,
链Webpack:配置u003d> {
config.output.globalObject('this')
}
}
配置完你会发现没有报错,然后就可以正常运行了!
到此结束。希望猿友可以一键连接三遍~~~
更多推荐




所有评论(0)