由于公司业务需求需要实现在微信小程序中实现语音测评功能,因为之前在H5中已经实现了该功能认为在小程序中问题不大,但是在实际开发中遇到了不少坑。

  1. 问题一:语音测评流式版在微信小程序中无法正确的传输数据并获取返回值

在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 };

以上就是我实现微信小程序进行语音测评 并将语音文件长传阿里云的实践过程

微信小程序接入科大讯飞实现语音测评相关推荐

  1. 微信支持环信_环信客户互动云v5.39已发布:支持微信小程序接入

    环信客户互动云v5.39_产品更新说明 发布日期:2018-11-06 客服模式 质检中新增会话ID字段 质检中新增会话ID字段,与历史会话中的会话ID对应,支持根据会话ID搜索质检会话,以及在质检详 ...

  2. 微信小程序接入第三方插件腾讯位置服务地图选点

    微信小程序接入第三方插件腾讯位置服务地图选点 1.在小程序服务平台中添加"腾讯位置服务地图选点"插件 1.在小程序服务平台中添加"腾讯位置服务地图选点"插件 微 ...

  3. uni-app 微信小程序接入高德SDK

    uni-app 微信小程序接入高德SDK 参考文档:https://lbs.amap.com/api/wx/gettingstarted 一.获取高德Key 配置高德key 二.获取高德key的操作步 ...

  4. 【物联网】微信小程序接入阿里云物联网平台

    微信小程序接入阿里云物联网平台 一 阿里云平台端 1.登录阿里云 阿里云物联网平台 点击进入公共实例,之前没有的点进去申请 2.点击产品->创建产品 3.产品名称自定义,按项目选择类型,节点类型 ...

  5. 微信小程序接入关联微信公众号official-account方案总结

    微信小程序接入关联微信公众号official-account方案总结 需求描述: 最近在小程序项目中有这样的需求,在微信小程序里面显示出关联的微信公众号,让用户在小程序一步直达公众号. 解决方案: 第 ...

  6. 微信小程序接入emq

    微信小程序接入emq 环境 emq v2.3.0 lnmp 微信开发工具 ubutun 16.04 1. 下载demo 微信小程序 MQTT 接入Demo下载地址 下载完成后,打开工程,修改index ...

  7. 微信小程序接入微信支付(二):后台调用统一下单接口

    微信统一支付官方文档:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1 因该接口需要商户系统中自己的订单编号,笔者先 ...

  8. 微信小程序接入腾讯云IM即时通讯(获取聊天历史记录开发步骤)

    微信小程序接入腾讯云IM即时通讯(获取聊天历史记录开发步骤) 1.先看文档: 获取 C2C 历史消息 :https://cloud.tencent.com/document/product/269/1 ...

  9. 微信小程序接入微信支付(三):小程序端调用支付接口

    微信小程序调用支付接口官方文档:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5 在上一节中 ...

最新文章

  1. 2022图机器学习必读的11大研究趋势和方向: 微分方程/子图表示/图谱理论/非对称/动态性/鲁棒性/通用性/强化学习/图量子等...
  2. 跳出数据计算拯救人智能
  3. python基础网易_十年Python大牛花了三天总结出来的python基础知识实例,超详细!...
  4. val, lazy, def
  5. HDU - 4507 吉哥系列故事——恨7不成妻(数位dp)
  6. java 7.0下载_Java jre 7.0
  7. Struts2 自定义验证器
  8. 谷歌浏览器chrome安装Hackbar插件方式
  9. DevExtreme 移动跨平台开发 C#语言
  10. Day01-计算机入门
  11. 【转】Java 5种字符串拼接方式性能比较。
  12. [Unity官方教程]Tanks!单机双人坦克大战源码和素材
  13. PBOOT网站后太登录显示验证码错误的解决经验分享
  14. 【计算机网络】计算机网络的体系结构
  15. STM32学习之Keil5软件配置
  16. INT201 决策,计算,语言 笔记
  17. 自考02323《操作系统概论》第一章操作系统简介——思维导图
  18. 2013渣打编程马拉松赛天津初赛总结
  19. EC11旋转编码器驱动程序
  20. 非线性方程的数值解法

热门文章

  1. 获得A股数据的交易接口代码
  2. Matlab系列之二维图形(上)
  3. html2canvas将html代码生成canvas转换成图片,并且保存到本地
  4. mysql 5.7 windows 密码重置_win10 mysql57密码重置
  5. java configuration类_JAVA中的Configuration类详解
  6. Ubuntu系统文件只读权限修改
  7. ubuntu14.04安装GTX 1080 ti显卡驱动以及遇到黑屏问题
  8. 个人碰到的前端问题总结及解决方法2
  9. c语言计算机二级常考点
  10. 基于简单统计的b站弹幕情感分析