微信小程序接入科大讯飞实现语音测评
由于公司业务需求需要实现在微信小程序中实现语音测评功能,因为之前在H5中已经实现了该功能认为在小程序中问题不大,但是在实际开发中遇到了不少坑。
- 问题一:语音测评流式版在微信小程序中无法正确的传输数据并获取返回值
在H5实现时使用 new WebSocket() 实例进行传输数据 并成功的拿到了测评的数据
ws = new WebSocket(url)
但是在小程序中 wx.connectSocket() 无法将数据流正常的传给科大讯飞的api
wx.connectSocket()
解决方案:使用微信录音功能将录音文件上传服务器,在通过服务端调用科大讯飞api进行语音测评得到结果后返回给前端
小程序代码如下:
// 将数据发送到服务器async _sendMp3(path) {this.data.toast.linShow({icon: "loading",title: '解析中请稍后',duration: 11111111,mask: true})wx.uploadFile({url: `${nodeServer}/xls/postData`, // node 服务接口filePath: path, // 录音文件临时地址header: {"Content-Type": "multipart/form-data" //必须是这个格式},formData: {text: this.data.XLSpeakerTitle // 要对比的文本},name: "mp3",success: async (res) => {const {Data,Code} = JSON.parse(res.data);if (Code == 200) {const score = parseInt(Data.score)this.setData({showScore: false,score,});// 开始动画this.countUp = new WxCountUp('score', score, {}, this);this.countUp.start();this._AddSpeakItem(score, Data.path);// 假如当前得分超过了历史最高分 将历史最高分更新this._UpDateMax(score)}},fail: () => {this.data.toast.linShow({title: '解析失败请,请稍后再试',})},complete: () => {this.data.toast.linHide()}})},
node js 服务端代码如下:
const router = require("koa-router")();
const { _init } = require("../ws/index");
const { put } = require("../ali-oss/index");const { removeFile } = require("../fs");
const { SuccessModel, ErrorModel } = require("../model/resModel");router.prefix("/xls");
router.post("/postData", async (ctx, next) => {const { text } = ctx.request.body;// 得到mp3 文件const path = ctx.request.files.mp3.path;// 得到对比分数const { Code, Data, Msg } = await _init(path, text);// 将上传的mp3 文件上传到 ali-oss const mp3path = await put(path);// 将上传的文件删除 防止上传的录音文件越来越多 导致服务器内存压力removeFile(path);if (Code == 0) {ctx.body = new SuccessModel({score: Data,path: mp3path.url.split("aliyuncs.com/")[1],});} else {ctx.body = new ErrorModel(Code, Msg ? Msg : "分数解析失败,请稍后再试");}
});
科大讯飞语音测评
const CryptoJS = require("crypto-js");
const WebSocket = require("ws");
var fs = require("fs");const APPID = "xxxxxxxxx";
const API_SECRET = "xxxxxxxxxx";
const API_KEY = "xxxxxxxxxx";
// 系统配置
let xmlParser = require("../util/parser");const config = {// 请求地址hostUrl: "wss://ise-api.xfyun.cn/v2/open-ise",host: "iat-api.xfyun.cn",//在控制台-我的应用-语音评测(流式版)获取appid: APPID,//在控制台-我的应用-语音评测(流式版)获取apiSecret: API_SECRET,//在控制台-我的应用-语音评测(流式版)获取apiKey: API_KEY,file: "./read_sentence_cn.pcm", //请填写您的音频文件路径uri: "/v2/open-ise",highWaterMark: 1280,
};
// 帧定义
const FRAME = {STATUS_FIRST_FRAME: 0,STATUS_CONTINUE_FRAME: 1,STATUS_LAST_FRAME: 2,
};// 设置当前临时状态为初始化
let status = FRAME.STATUS_FIRST_FRAME;
let ws;
const _init = (path, text) => {// 一定要初始化状态status = FRAME.STATUS_FIRST_FRAME;// 合成websocket接口,一次请求成功,未中断链接情况下,再次发送新请求导致 10140 错误const url = getWsUrl();console.log("url-----------", url);ws = new WebSocket(url);return new Promise((resolve, reject) => {// 连接建立完毕,读取数据进行识别ws.on("open", (event) => {console.log("websocket connect!");// config.filevar readerStream = fs.createReadStream(path, {highWaterMark: config.highWaterMark,});readerStream.on("data", function (chunk) {send(chunk, text);});// 最终帧发送结束readerStream.on("end", function () {status = FRAME.STATUS_LAST_FRAME;send("");});});// 得到识别结果后进行处理,仅供参考,具体业务具体对待ws.on("message", (data, err) => {if (err) {ws.close();return;}res = JSON.parse(data);if (res.code != 0) {ws.close();// reject(`error code ${res.code}, reason ${res.message}`);reject({ Code: res.code, Msg: res.message });return;}if (res.data.status == 2) {const { data } = res.data;let b = new Buffer(data, "base64");let iseResult = `最终识别结果: ${b.toString()}`;const scroeData = xmlParser.parse(iseResult, {attributeNamePrefix: "",ignoreAttributes: false,});resolve({Code: 0,Data:scroeData.xml_result.read_sentence.rec_paper.read_sentence.total_score,});}});// 资源释放ws.on("close", () => {console.log("connect close!-------------------------");});// 建连错误ws.on("error", (err) => {reject("websocket connect err: " + err);console.log("websocket connect err: " + err);});});
};
// 获取长连接地址
function getWsUrl() {// 获取当前时间 RFC1123格式let date = new Date().toUTCString();const wssUrl =config.hostUrl +"?authorization=" +getAuthStr(date) +"&date=" +date +"&host=" +config.host;return wssUrl;
}
// 传输数据
function send(data, text = "今天天气怎么样") {let frame = "";switch (status) {case FRAME.STATUS_FIRST_FRAME:// 第一次数据发送:frame = {common: { app_id: config.appid },business: {// 服务类型指定 ise(开放评测)sub: "ise",// 中文:cn_vip 英文:en_vipent: "cn_vip",// 题型:句子朗读category: "read_sentence",// 待评测文本编码 utf-8text: `\uFEFF${text}`,// 待评测文本编码 utf-8 gbktte: "utf-8",// 跳过ttp直接使用ssb中的文本进行评测(使用时结合cmd参数查看),默认值truettp_skip: true,cmd: "ssb",aue: "lame",auf: "audio/L16;rate=16000",},data: { status: 0 },};ws.send(JSON.stringify(frame));// 后续数据发送frame = {common: { app_id: config.appid },business: { aus: 1, cmd: "auw", aue: "lame" },data: { status: 1, data: data.toString("base64") },};status = FRAME.STATUS_CONTINUE_FRAME;break;case FRAME.STATUS_CONTINUE_FRAME:frame = {common: { app_id: config.appid },business: { aus: 2, cmd: "auw", aue: "lame" },data: { status: 1, data: data.toString("base64") },};break;case FRAME.STATUS_LAST_FRAME:frame = {common: { app_id: config.appid },business: { aus: 4, cmd: "auw", aue: "lame" },data: { status: 2, data: data.toString("base64") },};break;}ws.send(JSON.stringify(frame));
}
// 鉴权签名
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;
}module.exports = { _init };
2.问题2:文本朗读功能,刚开始做的时候我们是使用科大讯飞的语音合成功能,由于科大讯飞是收费的 推荐大家使用微信小程序同声传译插件很好用而且是免费,如果日使用量很大的话可以申请大额数量,缺点是朗读的文字有大小限制,我测试了下大概是200 字左右,如果超过两百字需要手动截取,分批合成
WechatSI 使用微信同声传译插件
WechatSI.textToSpeech({lang: "zh_CN",tts: true,content: this.data.XLSpeakerTitle,success: (res) => {this.data.WechatSIStatus = "success"this._palyBGAduio(res.filename, "演示")},fail: (err) => {this.setData({isPlay: false,isPlayDemo: false})this.data.toast.linShow({title: err.msg,})}})
3.问题三实现最高分回放功能,这里我的解决方案是在解决方案一的的同时,将上传到服务器的录音文件同时上传到阿里云OSS 返回给前端阿里云的图片地址和当前语音的测评分数,前端判断当前得分是否超过最高分,如果超过最高分将最高分更新
阿里云OSS 上传如下:
const OSS = require("ali-oss");const client = new OSS({bucket: "xlxtimg",// region以杭州为例(oss-cn-hangzhou),其他region按实际情况填写。region: "oss-cn-beijing",// 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录RAM控制台创建RAM账号。accessKeyId: "xxxxxxxx",accessKeySecret: "xxxxxxxxxxxxxxxxxx",
});async function put(file) {try {//object-name可以自定义为文件名(例如file.txt)或目录(例如abc/test/file.txt)的形式,实现将文件上传至当前Bucket或Bucket下的指定目录。let result = await client.put(`xlsLee/${file}`, file);return result;} catch (e) {console.log(e);}
}
module.exports = { put };
以上就是我实现微信小程序进行语音测评 并将语音文件长传阿里云的实践过程
微信小程序接入科大讯飞实现语音测评相关推荐
- 微信支持环信_环信客户互动云v5.39已发布:支持微信小程序接入
环信客户互动云v5.39_产品更新说明 发布日期:2018-11-06 客服模式 质检中新增会话ID字段 质检中新增会话ID字段,与历史会话中的会话ID对应,支持根据会话ID搜索质检会话,以及在质检详 ...
- 微信小程序接入第三方插件腾讯位置服务地图选点
微信小程序接入第三方插件腾讯位置服务地图选点 1.在小程序服务平台中添加"腾讯位置服务地图选点"插件 1.在小程序服务平台中添加"腾讯位置服务地图选点"插件 微 ...
- uni-app 微信小程序接入高德SDK
uni-app 微信小程序接入高德SDK 参考文档:https://lbs.amap.com/api/wx/gettingstarted 一.获取高德Key 配置高德key 二.获取高德key的操作步 ...
- 【物联网】微信小程序接入阿里云物联网平台
微信小程序接入阿里云物联网平台 一 阿里云平台端 1.登录阿里云 阿里云物联网平台 点击进入公共实例,之前没有的点进去申请 2.点击产品->创建产品 3.产品名称自定义,按项目选择类型,节点类型 ...
- 微信小程序接入关联微信公众号official-account方案总结
微信小程序接入关联微信公众号official-account方案总结 需求描述: 最近在小程序项目中有这样的需求,在微信小程序里面显示出关联的微信公众号,让用户在小程序一步直达公众号. 解决方案: 第 ...
- 微信小程序接入emq
微信小程序接入emq 环境 emq v2.3.0 lnmp 微信开发工具 ubutun 16.04 1. 下载demo 微信小程序 MQTT 接入Demo下载地址 下载完成后,打开工程,修改index ...
- 微信小程序接入微信支付(二):后台调用统一下单接口
微信统一支付官方文档:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1 因该接口需要商户系统中自己的订单编号,笔者先 ...
- 微信小程序接入腾讯云IM即时通讯(获取聊天历史记录开发步骤)
微信小程序接入腾讯云IM即时通讯(获取聊天历史记录开发步骤) 1.先看文档: 获取 C2C 历史消息 :https://cloud.tencent.com/document/product/269/1 ...
- 微信小程序接入微信支付(三):小程序端调用支付接口
微信小程序调用支付接口官方文档:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5 在上一节中 ...
最新文章
- 2022图机器学习必读的11大研究趋势和方向: 微分方程/子图表示/图谱理论/非对称/动态性/鲁棒性/通用性/强化学习/图量子等...
- 跳出数据计算拯救人智能
- python基础网易_十年Python大牛花了三天总结出来的python基础知识实例,超详细!...
- val, lazy, def
- HDU - 4507 吉哥系列故事——恨7不成妻(数位dp)
- java 7.0下载_Java jre 7.0
- Struts2 自定义验证器
- 谷歌浏览器chrome安装Hackbar插件方式
- DevExtreme 移动跨平台开发 C#语言
- Day01-计算机入门
- 【转】Java 5种字符串拼接方式性能比较。
- [Unity官方教程]Tanks!单机双人坦克大战源码和素材
- PBOOT网站后太登录显示验证码错误的解决经验分享
- 【计算机网络】计算机网络的体系结构
- STM32学习之Keil5软件配置
- INT201 决策,计算,语言 笔记
- 自考02323《操作系统概论》第一章操作系统简介——思维导图
- 2013渣打编程马拉松赛天津初赛总结
- EC11旋转编码器驱动程序
- 非线性方程的数值解法