318 lines
8.6 KiB
Vue
318 lines
8.6 KiB
Vue
<template>
|
||
<view class="">
|
||
<!-- <button class="btn" shape="circle" type="info" @touchstart="openMedia" @touchend="stopMedia">按住
|
||
说话</button> -->
|
||
<!-- @click="handleShow" -->
|
||
<!-- <iatPopup v-model:moduleValue="show" /> -->
|
||
<!-- <view v-if="show" class="iating">
|
||
正在说话中....
|
||
</view> -->
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import CryptoJS from "crypto-js";
|
||
// 科大讯飞接口配置
|
||
const config = {
|
||
hostUrl: "wss://iat-api.xfyun.cn/v2/iat",
|
||
host: "iat-api.xfyun.cn",
|
||
appid: "331874e7",
|
||
apiSecret: "YTc3MDJmYjJjMjk3NTJhMGMzMTk5YTVl", //"你的秘钥",
|
||
apiKey: "ddf92cc17ecc9cda0a8cca5d4997e897", //"你的apikey",
|
||
uri: "/v2/iat",
|
||
highWaterMark: 1280,
|
||
// file: "./16k_10.pcm",
|
||
};
|
||
// 鉴权签名
|
||
function getAuthStr(date) {
|
||
let signatureOrigin = `host: ${config.host}\ndate: ${date}\nGET ${config.uri} HTTP/1.1`;
|
||
let signatureSha = CryptoJS.HmacSHA256(signatureOrigin, config.apiSecret);
|
||
let signature = CryptoJS.enc.Base64.stringify(signatureSha);
|
||
let authorizationOrigin =
|
||
`api_key="${config.apiKey}", algorithm="hmac-sha256", headers="host date request-line", signature="${signature}"`;
|
||
let authStr = CryptoJS.enc.Base64.stringify(
|
||
CryptoJS.enc.Utf8.parse(authorizationOrigin)
|
||
);
|
||
return authStr;
|
||
}
|
||
|
||
function getUrl() {
|
||
// 获取当前时间 RFC1123格式
|
||
let date = new Date().toUTCString();
|
||
//科大讯飞远程连接地址
|
||
let wssUrl =
|
||
config.hostUrl +
|
||
"?authorization=" +
|
||
getAuthStr(date) +
|
||
"&date=" +
|
||
encodeURIComponent(date) +
|
||
"&host=" +
|
||
config.host;
|
||
console.log("websocke科大讯飞的地址为", wssUrl);
|
||
return wssUrl;
|
||
}
|
||
let uniSocketTask = null
|
||
//发送给科大讯飞的第一帧的模板数据格式
|
||
let frame = {
|
||
common: {
|
||
app_id: config.appid,
|
||
},
|
||
business: {
|
||
language: "zh_cn",
|
||
domain: "iat",
|
||
accent: "mandarin",
|
||
dwa: "wpgs", // 可选参数,动态修正
|
||
vad_eos: 5000,
|
||
},
|
||
data: {
|
||
status: 0,
|
||
format: "audio/L16;rate=16000",
|
||
encoding: "lame"
|
||
},
|
||
};
|
||
|
||
// 录音配置项
|
||
const recordOption = {
|
||
sampleRate: 16000, // 采样率(pc不支持)
|
||
format: "mp3", // 音频格式,默认是 aac
|
||
};
|
||
// 录音管理器
|
||
const recordManager = uni.getRecorderManager();
|
||
console.log('recordManager:', recordManager)
|
||
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
show: false,
|
||
resultText: "",
|
||
resultTextTemp: "",
|
||
renderText: "",
|
||
}
|
||
},
|
||
onLoad() {
|
||
|
||
},
|
||
onShow() {
|
||
|
||
},
|
||
methods: {
|
||
//打开麦克风
|
||
openMedia() {
|
||
this.connectSocket(); //创建websocket连接
|
||
},
|
||
stopMedia() {
|
||
console.log('组件录音结束',)
|
||
recordManager.stop()
|
||
},
|
||
//创建连接并返回数据
|
||
connectSocket() {
|
||
//创建socketTask实例
|
||
if (uniSocketTask === null) {
|
||
uniSocketTask = uni.connectSocket({
|
||
url: getUrl(),
|
||
success() {},
|
||
});
|
||
console.log('uniSocketTask:::', uniSocketTask)
|
||
//监听连接成功的事件
|
||
uniSocketTask.onOpen(() => {
|
||
console.log("监听到开启连接成功");
|
||
this.startRecord(); //打开录音
|
||
});
|
||
//监听连接关闭的事件
|
||
uniSocketTask.onClose((res) => {
|
||
console.log("监听到关闭连接成功", res); //Websocket关闭!服务器读取MSG超时
|
||
uniSocketTask = null;
|
||
});
|
||
uniSocketTask.onError((res) => {
|
||
console.log("监听到连接发生错误", res);
|
||
});
|
||
//监听科大讯飞消息返回
|
||
uniSocketTask.onMessage((res) => {
|
||
//收到消息
|
||
console.log('收到消息:', res)
|
||
const message = JSON.parse(res.data);
|
||
//判断是否存在数据
|
||
if (res.data) {
|
||
console.log("收到服务器消息,并开始渲染", message);
|
||
this.renderResult(message);
|
||
|
||
if (message.code === 0 && message.data.status === 2) {
|
||
//该函数为当前页唯一的关闭连接函数
|
||
console.log('最后一条', this.renderText)
|
||
this.closeSocket();
|
||
this.$emit('renderText', this.renderText)
|
||
}
|
||
//收到不正常服务器消息,返回错误到控制台
|
||
if (message.code !== 0) {
|
||
this.closeSocket();
|
||
console.error(message);
|
||
}
|
||
} else {
|
||
console.log("未监听到消息:原因:", JSON.stringify(res));
|
||
}
|
||
});
|
||
} else {
|
||
console.log("socketTask实例已存在");
|
||
}
|
||
},
|
||
//发送消息
|
||
sendMessage(sendData) {
|
||
console.log('发送', JSON.stringify(sendData))
|
||
uniSocketTask.send({
|
||
data: JSON.stringify(sendData),
|
||
success() {
|
||
console.log("发送成功");
|
||
},
|
||
fail() {
|
||
console.log("发送失败");
|
||
},
|
||
});
|
||
},
|
||
//关闭连接
|
||
closeSocket() {
|
||
console.log("开始尝试关闭连接");
|
||
// 关闭连接
|
||
uniSocketTask.close();
|
||
},
|
||
//开启录音
|
||
startRecord() {
|
||
console.log("打开录音");
|
||
recordManager.onStart(() => {
|
||
console.log("开始录音");
|
||
this.show = true
|
||
// ...
|
||
});
|
||
recordManager.onStop((res) => {
|
||
this.show = false
|
||
// tempFilePath String 录音文件的临时路径
|
||
console.log("录音停止,文件路径为:", res.tempFilePath);
|
||
|
||
// this.sendMessage(frame)
|
||
|
||
this.pathToBase64(res.tempFilePath).then(base64 => {
|
||
console.log('base64--', base64);
|
||
// 这里一定要注意:当需要将base64字符串转换为Buffer时,通常会去掉前面部分的数据URL标识(如"data:image/png;base64,"),因为这部分内容不是实际的base64编码数据。
|
||
let buff = base64.split(",")[1]
|
||
const arrayBuffer = uni.base64ToArrayBuffer(buff)
|
||
const audioString = this.toString(arrayBuffer)
|
||
console.log("文件读取成功", audioString.length);
|
||
let offset = 0;
|
||
while (offset < audioString.length) {
|
||
const subString = audioString.substring(offset, offset + 1280)
|
||
offset += 1280
|
||
const isEnd = offset >= audioString.length
|
||
|
||
let params = {
|
||
common: {
|
||
app_id: config.appid,
|
||
},
|
||
business: {
|
||
language: "zh_cn",
|
||
domain: "iat",
|
||
accent: "mandarin",
|
||
dwa: "wpgs", // 可选参数,动态修正
|
||
vad_eos: 5000,
|
||
},
|
||
data: {
|
||
status: isEnd ? 2 : 1,
|
||
format: "audio/L16;rate=16000",
|
||
encoding: "lame",
|
||
audio: btoa(subString)
|
||
}
|
||
}
|
||
|
||
this.sendMessage(params);
|
||
}
|
||
}).catch(error => {
|
||
console.error(error)
|
||
})
|
||
|
||
});
|
||
recordManager.onError((err) => {
|
||
// errMsg String 错误信息
|
||
console.log("录音出现错误", err);
|
||
});
|
||
|
||
recordManager.start(recordOption);
|
||
},
|
||
//关闭录音
|
||
stopRecord() {
|
||
recordManager.stop();
|
||
},
|
||
toString(buffer) {
|
||
var binary = '';
|
||
var bytes = new Uint8Array(buffer);
|
||
var len = bytes.byteLength;
|
||
for (var i = 0; i < len; i++) {
|
||
binary += String.fromCharCode(bytes[i]);
|
||
}
|
||
return binary
|
||
},
|
||
//录音文件路径转base64
|
||
pathToBase64(path) {
|
||
return new Promise(function(resolve, reject) {
|
||
// app
|
||
if (typeof plus === 'object') {
|
||
plus.io.resolveLocalFileSystemURL(path, function(entry) {
|
||
entry.file(function(file) {
|
||
var fileReader = new plus.io.FileReader()
|
||
fileReader.onload = function(evt) {
|
||
resolve(evt.target.result)
|
||
}
|
||
fileReader.onerror = function(error) {
|
||
reject(error)
|
||
}
|
||
fileReader.readAsDataURL(file)
|
||
}, function(error) {
|
||
reject(error)
|
||
})
|
||
}, function(error) {
|
||
reject(error)
|
||
})
|
||
|
||
return
|
||
}
|
||
|
||
reject(new Error('not support'))
|
||
})
|
||
},
|
||
//讯飞回复字段拼接
|
||
renderResult(jsonData) {
|
||
console.log("开始执行渲染函数", jsonData);
|
||
if (jsonData.data && jsonData.data.result) {
|
||
let data = jsonData.data.result;
|
||
let str = ""; // 初始化一个字符串变量用于存储拼接后的识别结果
|
||
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.resultText = this.resultTextTemp;
|
||
}
|
||
// 将结果存储在resultTextTemp中
|
||
this.resultTextTemp = this.resultText + str;
|
||
} else {
|
||
this.resultText = this.resultText + str;
|
||
}
|
||
this.renderText = this.resultTextTemp || this.resultText || "";
|
||
}
|
||
console.log("渲染后的数据为");
|
||
console.log(this.renderText);
|
||
},
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
page {
|
||
/* background-color: #111111; */
|
||
}
|
||
</style>
|
||
<style lang="scss" scoped>
|
||
|
||
</style> |