- 微信小程序使用科大讯飞语音评测,保姆级教程!
最近微信小程序项目中,需要添加语音评测功能,就选用了科大讯飞的语音评测流式版接口,但在使用过程中,遇到了很多问题,再网上搜资料,搜了好多,也没直接能用的,好在后来参考了许多资料后,终于调试成功了,接下来,跟大家分享一下我是怎么处理的。 1.第一步,准备所用到的工具,下载官方jsdemo,将 base64js 文件复制到自己的小程序项目中,用npm安装crypto-js xmldom这2个工具 然后,将工具导入到页面中 const CryptoJS = require('crypto-js') const Base64 = require('../../tools/base64js').Base64; var DOMParser = require('xmldom').DOMParser; 2.第二步,初始化用到的变量,定义用到的关键函数 const APPID = '替换成你自己的' const API_SECRET = '替换成你自己的' const API_KEY = '替换成你自己的' let audioData = [] //存储音频流的数组 let socketTask = null //小程序的socketTask let handlerInterval = null // 定时器,用来定时发送数据流 function getWebSocketUrl() {//生成socket使用的url return new Promise((resolve, reject) => { var url = 'wss://ise-api.xfyun.cn/v2/open-ise' var host = 'ise-api.xfyun.cn' var apiKey = API_KEY var apiSecret = API_SECRET var date = new Date().toGMTString() var algorithm = 'hmac-sha256' var headers = 'host date request-line' var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/open-ise HTTP/1.1` var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret) var signature = CryptoJS.enc.Base64.stringify(signatureSha) var authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"` var authorization =Base64.encode(authorizationOrigin) url = `${url}?authorization=${authorization}&date=${date}&host=${host}` resolve(url) }) } 3.开始录音 //开始录音 startVoiceRecord(){ let that = this that.setData({recordState:'recording'}) recorderManager.onStart(() => { console.log('recorder start') }) recorderManager.onPause(() => { console.log('recorder pause') }) recorderManager.onStop((res) => { console.log('recorder stop', res) const { tempFilePath } = res that.startUpRecord()//录音完成,准备调用讯飞接口 }) recorderManager.onFrameRecorded((res) => { const { frameBuffer } = res console.log('frameBuffer.byteLength', frameBuffer.byteLength) let u8Arr = new Uint8Array(frameBuffer) audioData.push(u8Arr) //将每一帧的数据取出,放到audioData中,准备使用 }) const options = { duration: 180000, sampleRate: 16000, numberOfChannels: 1, encodeBitRate: 44100, frameSize: 2, format: 'pcm', } recorderManager.start(options) }, 4. 开始socket连接,准备上传数据并处理 startUpRecord(){ let that = this getWebSocketUrl().then(( url)=>{ let newURL = encodeURI(url) socketTask = wx.connectSocket({ url: newURL, }) socketTask.onOpen(()=>{ console.log('打开了socket') that.webSocketSend() }) socketTask.onMessage((e)=>{ // result 在这里做信息处理 console.log('收到了结果:',e) that.result(e.data) }) socketTask.onError((err)=>{ //结束录音 console.log('socket 出错:',err) }) socketTask.onClose(()=>{ // 结束录音 console.log('socket 关闭:') }) }) }, webSocketSend() { console.log('开始发送数据',audioData) let that = this let audioDataUp = audioData.splice(0, 1) var params = { common: { app_id:APPID, }, business: { category: 'read_sentence', // read_syllable/单字朗读,汉语专有 read_word/词语朗读 read_sentence/句子朗读 https://www.xfyun.cn/doc/Ise/IseAPI.html#%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B rstcd: 'utf8', group: 'pupil', sub: 'ise', ent: 'cn_vip', tte: 'utf-8', cmd: 'ssb', auf: 'audio/L16;rate=16000', aus: 1, aue: 'raw', text: '\uFEFF' + '今天天气怎么样?' }, data: { status: 0, encoding: 'raw', data_type: 1, data: that.toBase64(audioDataUp[0]), }, } console.log(JSON.stringify(params)) socketTask.send({data: JSON.stringify(params)}) handlerInterval = setInterval(() => { // websocket未连接 if (!socketTask) { clearInterval(handlerInterval) return } // 最后一帧 if (audioData.length === 0) { console.log('数据发送完毕') socketTask.send( {data: JSON.stringify({ business: { cmd: 'auw', aus: 4, aue: 'raw' }, data: { status: 2, encoding: 'raw', data_type: 1, data: '', }, })} ) audioData = [] clearInterval(handlerInterval) return false } audioDataUp = audioData.splice(0, 1) // 中间帧 console.log('audioDataUp:',audioDataUp[0]) socketTask.send( { data: JSON.stringify({ business: { cmd: 'auw', aus: 2, aue: 'raw' }, data: { status: 1, encoding: 'raw', data_type: 1, data: that.toBase64(audioDataUp[0]), }, })} ) }, 40) }, result(resultData) { // 识别结束 let jsonData = JSON.parse(resultData) if (jsonData.data && jsonData.data.data) { let data = Base64.decode(jsonData.data.data) const doc=new DOMParser().parseFromString(data,'text/xml'); let sentence = doc.getElementsByTagName('read_sentence')[1] let accuracy_score = sentence.getAttribute('accuracy_score') let emotion_score = sentence.getAttribute('emotion_score') let fluency_score = sentence.getAttribute('fluency_score') let total_score = sentence.getAttribute('total_score') let integrity_score = sentence.getAttribute('integrity_score') let phone_score = sentence.getAttribute('phone_score') let tone_score = sentence.getAttribute('tone_score') let is_rejected = sentence.getAttribute('is_rejected') console.log('parseRes:',accuracy_score,emotion_score,fluency_score,total_score) //评测结果在这里,就出来了,然后就可以拿评测数据去使用了 } if (jsonData.code === 0 && jsonData.data.status === 2) { // 在这里结束socket socketTask.close() } if (jsonData.code !== 0) { socketTask.close() console.log(`${jsonData.code}:${jsonData.message}`) } }, 到这里,整个流程就完了,祝愿大家都能一次调用成功,有什么问题的话,咱们再讨论!
2023-06-13 - 通过npm或useExtendedLib方式引入组件库
通过npm或useExtendedLib方式引入组件库1、需求需要引入weui组件库,组件库支持“useExtendedLib、npm install”两种方式引入2、两种方式引入两种方式是冲突的, 一个组件只能由一种方式引入. 通过 useExtendedLib 扩展库 的方式引入,这种方式引入的组件将不会计入代码包大小。也可以通过npm (opens new window)方式下载构建(一) 通过useExtendedLib的方式引入在 app.json 中添加 "useExtendedLib": { "weui" : true } [图片] (二) 通过npm构建的方式引入打开NPM组件使用[图片] 然后打开一下terminal[图片] npm 初始化[代码]npm init[代码] [图片] 然后一直回车即可,本次属于测试,不需要填写更多信息 安装weui-miniprogram[代码]npm install weui-miniprogram[代码] [图片] 构建NPM(此处必做,npm install 后是组件源码,需要构建后才能使用)[图片] 构建完成 [图片] 完成后的目录 [图片] 注意!如果微信小程序NPM构建失败提示找不到npm包位置,这时候需要前往project.config.json添加指向[图片] 修改[图片] [图片] "packNpmManually": true, "packNpmRelationList": [ { "packageJsonPath": "./package.json", "miniprogramNpmDistDir": "./node_modules" } ] [图片] 通过app.wxss引入全局样式@import '/miniprogram_npm/weui-miniprogram/weui-wxss/dist/style/weui.wxss'; [图片] (三)效果预览使用官方的表单预览,代码:Form | 微信开放文档 (qq.com) [图片] (四)小问题修复预览图中,[代码]page__hd[代码]、[代码]page__title[代码]、[代码]page__desc[代码]的样式和官方预览图是不一样的 <view class="page__hd"> <view class="page__title">Form</view> <view class="page__desc">表单输入</view> </view> 修复,新建[代码]page__hd[代码]、[代码]page__title[代码]、[代码]page__desc[代码]的样式, 这样就舒服多了 [图片] .title{ cursor: default; user-select: none; line-height: 1.6; --height: 44px; --right: 95px; font-family: -apple-system-font,Helvetica Neue,Helvetica,sans-serif; margin: 0; display: block; text-align: left; font-size: 20px; font-weight: 400; } .hd{ cursor: default; user-select: none; line-height: 1.6; --height: 44px; --right: 95px; font-size: 16px; font-family: -apple-system-font,Helvetica Neue,Helvetica,sans-serif; margin: 0; display: block; padding: 40px; } .desc{ cursor: default; user-select: none; line-height: 1.6; --height: 44px; --right: 95px; font-family: -apple-system-font,Helvetica Neue,Helvetica,sans-serif; margin: 0; display: block; margin-top: 5px; color: #888888; text-align: left; font-size: 14px; } 将样式加到组件中 <view class="page__hd hd"> <view class="page__title title">Form</view> <view class="page__desc desc">表单输入</view> </view> 同时下面隐私协议里面的复选框也不见了[图片] 找到发现是[代码]weui-agree__checkbox[代码]和[代码]weui-agree__checkbox-icon[代码]样式缺失,终于看得见框框了 [图片] .agreeCheckBox{ cursor: default; user-select: none; line-height: 1.6; --height: 44px; --right: 95px; font-family: -apple-system-font,Helvetica Neue,Helvetica,sans-serif; font-size: 13px; color: rgba(0,0,0,.5); margin: 0; -webkit-tap-highlight-color: transparent; display: inline-block; position: absolute; left: -9999px; } .agreeCheckBoxIcon{ cursor: default; user-select: none; line-height: 1.6; --height: 44px; --right: 95px; font-family: -apple-system-font,Helvetica Neue,Helvetica,sans-serif; font-size: 13px; color: rgba(0,0,0,.5); margin: 0; position: relative; top: 2px; display: inline-block; border: 1px solid #d1d1d1; background-color: #fff; border-radius: 3px; width: 11px; height: 11px; } 将样式加到组件中 <checkbox-group bindchange="bindAgreeChange" class="checkBoxGroup"> <label class="weui-agree agree" for="weuiAgree"> <view class="weui-agree__text agreeText"> <checkbox class="weui-agree__checkbox agreeCheckBox" id="weuiAgree" value="agree" checked="{{isAgree}}" /> <view class="weui-agree__checkbox-icon agreeCheckBoxIcon"> <icon class="weui-agree__checkbox-icon-check" type="success_no_circle" size="9" wx:if="{{isAgree}}"></icon> </view>阅读并同意<navigator url="" class="weui-agree__link">《相关条款》</navigator> </view> </label> </checkbox-group>
2021-11-10 - 如何彻底解决小程序滚动穿透问题
背景 俗话说,产品有三宝:弹窗、浮层加引导,足以见弹窗在产品同学心目中的地位。对任意一个刚入门的前端同学来说,实现一个模态框基本都可以达到信手拈来的地步,但是,当模态框里边的内容滚动起来以后,就会出现各种各样的让人摸不着头脑的问题,其中,最出名的想必就是滚动穿透。 什么是滚动穿透? 滚动穿透的定义:指我们滑动顶层的弹窗,但效果上却滑动了底层的内容。 具体解决方案分析如下: 改变顶层:从穿透的思路考虑,如果顶层不会穿透过去,那么问题就解决了,所以我们尝试给蒙层加catchtouchmove,但是发现部分场景无效果,那么就不再赘述了。 改变底层:既然是顶层影响了底层,要是底层不会滚动,那就没这个问题了。 如何改变底层解决该问题呢? 不成熟方案: 底部页面最外层view设置position: fixed;页面不可滚动,但是这个时候会导致页面回到顶部。 滚动时监听滚动距离,弹窗时记录滚动位置,关闭弹窗后使用wx.pageScrollTo回滚到记录的位置。 成熟方案 使用page-meta组件,通过该组件我们可以操作Page的style样式,类似于h5里body设置overflow: hidden; 控制页面不可滚动。文档地址:https://developers.weixin.qq.com/miniprogram/dev/component/page-meta.html 使用wx.setPageStyle设置overflow: hidden, 也可以实现给Page组件设置样式。) page-meta组件: 通过该组件我们可以直接操作[代码]Page[代码]组件 ,我们给它的wxss样式overflow动态设置[代码]hidden[代码]or[代码]visible[代码]or[代码]auto[代码] 就可以控制整个页面是否可以滚动。 [图片] wx.setPageStyle方法: 调用这个api,动态设置它为hidden/auto,用于控制页面是否可滚动,主要用于页面组件内使用,比如封装好的弹窗组件,就不用单独写page-meta组件了。。 [代码]wx.setPageStyle({ style: { overflow: 'hidden' // ‘auto’ } }) [代码] 老规矩,结尾放代码片段: https://developers.weixin.qq.com/s/U6ItgQmP7upQ 拓展 支付宝小程序虽然存在page-meta组件,但是由于内核为69版本,给page设置overflow: hidden 也无法控制底部元素不可滚动,目前已联系支付宝的底层开发同学提供API控制页面disableScroll,目前正在封装Appx,近期开放。
08-06