赞
踩
由于公司业务需求需要实现在微信小程序中实现语音测评功能,因为之前在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()
- }
- })
- },
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
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 : "分数解析失败,请稍后再试");
- }
- });
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
科大讯飞语音测评
- 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.file
- var 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_vip
- ent: "cn_vip",
- // 题型:句子朗读
- category: "read_sentence",
- // 待评测文本编码 utf-8
- text: `\uFEFF${text}`,
- // 待评测文本编码 utf-8 gbk
- tte: "utf-8",
- // 跳过ttp直接使用ssb中的文本进行评测(使用时结合cmd参数查看),默认值true
- ttp_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 };
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
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,
- })
- }
- })
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
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 };
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
以上就是我实现微信小程序进行语音测评 并将语音文件长传阿里云的实践过程
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。