本文主要是介绍微信小程序调用讯飞接口实现录音和获取录音音译的文本 前端部分 包含图片、视频的上传功能,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
时间的3是三秒还没修改格式 问题不大 凑合着看一眼弄了个啥 单纯为了记录 需要的同学得自己找需用用到的地方 我多少写了点注释
wxml部分:
<view class="wrap"><el-form :inline="true" :model="form"><view class="white-box" wx:for="{{activityMaterials}}" wx:key="index"><view class="title-box"><view class="yellow-fence"></view><view class="title-txt-part" wx:if="{{!item.isEdit}}"><view class="v1">{{item.title}}</view><image src="/image/icon_edit.png" class="icon-edit" data-idx="{{index}}" bindtap="titleHandle" /></view><input wx:if="{{item.isEdit}}" type="text" value="" data-idx="{{index}}" class="title-input" placeholder="请输入标题" bindblur="inputBlurHandle" /> <image src="/image/icon_close_black.png" data-idx="{{index}}" class="icon-close-black" bindtap="deleteHandle"></image> </view><view class="textarea-box-wrap"><view class="voice-box" wx:if="{{item.recordArr.length > 0}}"><!--这里是录音的显示区域--><view class="record-item" wx:for="{{item.recordArr}}" wx:key="txt" wx:for-item="oItem" wx:for-index="oIndex"><view class="record-list" data-pidx="{{index}}" data-idx="{{oIndex}}" bindtap="playHandle"> <image src="{{oItem.played == true ? '/image/icon_pause.png' : '/image/icon_video.png'}}" class="icon-video" /><text class="t1">语音</text><text class="t2">00:{{oItem.time}}</text></view><view class="record-txt" wx:if="{{oItem.showTxt}}">{{oItem.txt}}</view></view><!-- <view class="record-item"><view class="record-list"> <image src="/image/icon_video.png" class="icon-video" /><text class="t1">语音</text><text class="t2">00:23</text></view><view class="record-txt">该生聪明伶俐,乐于助人,和同学们相处融洽,尊师重道,就是不学习,感觉没别的啥缺点</view></view> --></view><textarea class="textarea-box" value="{{item.content}}" data-idx="{{index}}" bindblur="textareaBlurHandle" placeholder="请输入内容"></textarea><!--这里是中间那个录音按钮--><button class="record-btn" data-idx="{{index}}" bindtap="recordHandle"><image src="/image/icon_record_gray.png" wx:if="{{!item.recordState}}" class="icon-record" /><image src="/image/icon_record_red.png" wx:else="{{item.recordState}}" class="icon-record" /></button></view><view class="material-box"><button class="upload-box" data-idx="{{index}}" bindtap="uploadFile"><image src="/image/icon_photo.png" class="icon-photo" />上传图片、视频</button><block wx:for="{{activityMaterials[index].material}}" wx:for-item="mItem" wx:for-index="mIndex" wx:key="url"><view class="material-list" wx:if="{{mItem.type == 'img'}}"><image src="/image/icon_close.png" class="icon-close" data-pidx="{{index}}" data-idx="{{mIndex}}" bindtap="closeHadle" /><image src="{{mItem.url}}" data-src="{{mItem.url}}" data-pidx="{{index}}" data-idx="{{mIndex}}" bindtap="preview" class="material-list-m2" mode="aspectFill"></image></view><view class="material-list" wx:if="{{mItem.type == 'mp4'}}"><image src="/image/icon_close.png" class="icon-close" data-pidx="{{index}}" data-idx="{{mIndex}}" bindtap="closeHadle" /><video src="{{mItem.url}}" data-src="{{mItem.url}}" class="material-list-m2" mode="aspectFill"></video></view></block></view></view><view class="add-box" bindtap="addListHandle">+添加</view><view class="save-btn" bindtap="save">提交</view></el-form>
</view>
js部分
const app = getApp()
const util = require('../../utils/util.js');
const api = require('../../config/api.js');
const BASE_URL = app.globalData.BASE_URL
let lock = false//**************重点 该录音功能亲测如果超过三秒不继续录音会自动结束录音 还得手动关闭录音状态**************
//录音部分-start
const innerAudioContext = wx.createInnerAudioContext({useWebAudioImplement: false // 是否使用 WebAudio 作为底层音频驱动,默认关闭。对于短音频、播放频繁的音频建议开启此选项,开启后将获得更优的性能表现。由于开启此选项后也会带来一定的内存增长,因此对于长音频建议关闭此选项
})
const recorderManager = wx.getRecorderManager();
var wxst; //语音websocket
var status = 0; // 音频的状态
var iatResult = [] // 识别结果
const searchoptions = {duration: 1000 * 60,sampleRate: 16000,numberOfChannels: 1,encodeBitRate: 48000,format: 'mp3',frameSize: 6
}
//录音部分-endPage({data: {coursewareId: '',form: {},activityMaterials: [],isFromReport: false,info: {}, //recordArr: [], recordArr率属于activityMaterials的子集 //音频数组 包含三个字段 url->mp3格式文件 txt:MP3的文字版的文本 time: 录音的时间 played:是否正在播放parentIndex: null,//录音部分-startsrcMic: '',edit_text_max: 200,remain_length: 200,edit_text: "",is_focus: false,tips: "",index: -1,voicePath: "",//语音//recordState: false, //录音状态 该字段纳入作为activityMaterials的子级 由activityMaterials统一调配contentTxt: '', //内容firstSend: true,//录音部分-end},onLoad(options) {//console.log(options)if (options.title) { //自定义顶部导航titlewx.setNavigationBarTitle({title: options.title})}const userInfo = wx.getStorageSync('userInfo');this.setData({userInfo,coursewareId: options.coursewareId,isFromReport: options.isFromReport ? true : false,title: options.title,info: {thumb: options.thumb,title: options.title,updateTime: options.updateTime},activityMaterials: []})lock = false//先调用接口 如果length为零 那么新增一个对象 this.loadData()this.initRecord()//这个是录音的内容要调用 },onShow: function () { //这是录音的马蜂窝var that = this;recorderManager.onStart(() => {//开始录音时触发status = 0;iatResult = []//console.log('recorder start');that.data.firstSend = false;});recorderManager.onError((res) => {//错误回调//console.log(res);});recorderManager.onStop((res) => {//结束录音时触发//console.log('recorder stop')//console.log(res)let otime = (res.duration/1000).toFixed(0)status = 2;var sendsty = '{"data":{"status":2,"audio":"","format":"audio/L16;rate=8000","encoding":"raw"}}'wxst.send({data: sendsty})wx.uploadFile({// 模拟httpsurl: BASE_URL + 'common/uploadMediaToTencent', //需要用HTTPS,同时在微信公众平台后台添加服务器地址filePath: res.tempFilePath, //上传的文件本地地址name: 'file',//附近数据,这里为路径success: function(result) { // console.log("--结束录音--")// console.log(that.data.contentTxt)// console.log(JSON.parse(result.data).url)// console.log(otime)if(that.data.contentTxt){ //recordArr: []//音频数组 包含三个字段 url->mp3格式文件 txt:MP3的文字版的文本 time: 录音的时间 played:是否正在播放let ourl = JSON.parse(result.data).urllet pidx = that.data.parentIndexlet params = {url: ourl,txt: that.data.contentTxt,time: that.timeHandle(otime),//这个时间是用来显示的played: false,showTxt: false,}let oarr = that.data.activityMaterials oarr.forEach((item) => {item.recordArr = item.recordArr && item.recordArr.length > 0 ? item.recordArr : []})oarr[pidx].recordArr.push(params)that.setData({activityMaterials: oarr}) //console.log(that.data.activityMaterials)}else{//console.log("木有内容啊")}},fail: function(err) {//console.log(err)}})});recorderManager.onFrameRecorded((res) => {//每帧触发const that = thisconst { frameBuffer, isLastFrame } = reslet params = {'common': {'app_id': '57198f05'},'business': {'language': 'zh_cn', //⼩语种可在控制台--语⾳听写(流式)--⽅⾔/语种处添加试⽤'domain': 'iat','accent': 'mandarin', //中⽂⽅⾔可在控制台--语⾳听写(流式)--⽅⾔/语种处添加试⽤'vad_eos': 1000,'dwa': 'wpgs' //为使该功能⽣效,需到控制台开通动态修正功能(该功能免费)},'data': {'status': 0,'format': 'audio/L16;rate=16000','encoding': 'lame','audio': wx.arrayBufferToBase64(frameBuffer)}}// 拼接数据let status = 0if (this.data.firstSend) {this.data.firstSend = false} else {if (isLastFrame) {status = 2} else {status = 1}}params.data.status = statuswx.sendSocketMessage({data: JSON.stringify(params),success: (data) => {console.log('send success:' + JSON.stringify(data))//that.resetRecordState()},fail: (err) => {console.log('send error:' + JSON.stringify(err))//中断录音的时候//that.resetRecordState()},completed: () => {if (isLastFrame) {// wx closeSocket}}})})},timeHandle(time){if(time < 9){return '0' + time}else{return time}},//重置录音按钮状态resetRecordState(){let activityMaterials = this.data.activityMaterialsactivityMaterials[this.data.parentIndex].recordState = falsethis.setData({activityMaterials: activityMaterials})},save() {//这里还没改 不重要的文件if (lock) returnlock = true//处理过滤activityMaterials数据let actArr = this.data.activityMaterials.map(function (item) {let oarr = []//转换数据格式oarr[0] = {content: item.content}for (let i in item.material) {oarr.push({content: item.material[i].url})}return {title: item.title,paragraphs: oarr}})for (let i in actArr) {//title等于编辑标题就代表没编辑过 paragraphs[0].content的值为空就代表textare没编辑过 paragraphs[1]不存在或者content为空就代表没编辑过if (actArr[i].title == '编辑标题' && !actArr[i].paragraphs[0].content && (!actArr[i].paragraphs[1] || !actArr[i].paragraphs[1].content)) {actArr.splice(i, 1)}}if (actArr.length == 0) {wx.showToast({title: '请先编辑材料!',icon: 'none',duration: 2000})lock = falsereturn}util.request(api.activityPreparationSave, {teacherId: this.data.userInfo.userId,classId: this.data.userInfo.deptId,coursewareId: this.data.coursewareId,activityMaterials: actArr}, 'POST', 'application/json').then(res => {if (res.code = 200) {wx.showToast({title: '提交成功!',icon: 'error',duration: 2000})//判断是不是报告那边跳转过来的if (this.data.isFromReport) {let oinfo = this.data.infowx.redirectTo({url: '../reportdetail/reportdetail?coursewareId=' + this.data.coursewareId + '&title=' + oinfo.title + '&thumb=' + oinfo.thumb + '&updateTime=' + oinfo.updateTime})} else {wx.navigateBack({delta: 1});}lock = false} else {wx.showToast({title: res.msg,icon: 'error',duration: 2000})lock = false}});},loadData() {util.request(api.activityPreparationList + this.data.userInfo.userId + '&coursewareId=' + this.data.coursewareId + '&classId=' + this.data.userInfo.deptId, '', 'get').then(res => {if (res.code = 200) {let odata = res.dataif (odata && odata.length == 0) {let obj = this.data.activityMaterialsobj.push({title: '编辑标题',isEdit: false,content: '',material: []})this.setData({activityMaterials: obj})} else {//重组数据//type 1图片 3视频 let oarr = odata.map(function (item) {let paragraphs = []for (let i in item.paragraphs) {paragraphs.push({url: item.paragraphs[i].content,type: item.paragraphs[i].type == '3' ? 'mp4' : item.paragraphs[i].type == '1' ? 'img' : ''})}return {title: item.title ? item.title : '编辑标题',isEdit: false,content: item.content,material: paragraphs}})//console.log(oarr)this.setData({activityMaterials: oarr})}} else {wx.showToast({title: res.msg,icon: 'error',duration: 2000})}});},addListHandle() {let obj = this.data.activityMaterialsobj.push({title: '编辑标题',isEdit: false,content: '',material: []})this.setData({activityMaterials: obj})},titleHandle(e) {let obj = this.data.activityMaterialslet idx = e.currentTarget.dataset.idxobj[idx].isEdit = truethis.setData({activityMaterials: obj})},inputBlurHandle(e) {let oval = e.detail.valuelet obj = this.data.activityMaterialslet idx = e.currentTarget.dataset.idxif (oval) {obj[idx].title = oval}obj[idx].isEdit = falsethis.setData({activityMaterials: obj})},textareaBlurHandle(e) {let oval = e.detail.valuelet obj = this.data.activityMaterialslet idx = e.currentTarget.dataset.idxif (oval) {obj[idx].content = oval}this.setData({activityMaterials: obj})},preview(e) {let currentUrl = e.currentTarget.dataset.srclet pidx = e.currentTarget.dataset.pidx //父级循环的indexconst oarr = []for (let i in this.data.activityMaterials[pidx].material) {oarr.push(this.data.activityMaterials[pidx].material[i].url)}wx.previewImage({current: currentUrl, // 当前显示图片的http链接urls: oarr // 需要预览的图片http链接列表})},closeHadle(e) {//console.log(e)const that = thiswx.showModal({title: '',content: '是否删除素材',success(res) {if (res.confirm) {let obj = that.data.activityMaterialslet pidx = e.currentTarget.dataset.pidx //父级循环的indexlet idx = e.currentTarget.dataset.idx //子级循环的indexobj[pidx].material.splice(idx, 1)that.setData({activityMaterials: obj})} else if (res.cancel) {//console.log('用户点击取消')}}})},//上传图片、视频uploadFile: function (e) {const that = thislet obj = this.data.activityMaterialslet pidx = e.currentTarget.dataset.idxwx.chooseMedia({count: 9, // 设置为需要上传的文件数量mediaType: ['image', 'video'],success: function (res) {const files = res.tempFiles;// 遍历选择的文件数组,依次上传每个文件files.forEach(function (file) {const tempFilePath = file.tempFilePath;wx.uploadFile({url: BASE_URL + 'common/uploadMediaToTencent',filePath: tempFilePath,name: 'file',success: function (res) {//console.log(res)let ores = JSON.parse(res.data)//每个数组里添加一个type值是mp4或者img,在for循环里通过type赋值到mp4或者img里面let otype = that.getFileExtension(ores.url)if (otype) {let params = {url: ores.url,type: otype}obj[pidx].material.push(params)}that.setData({activityMaterials: obj})},fail: function (res) {//console.log(res.errMsg);// 处理上传失败后的逻辑}})});}})},//用一个方法来判断 返回的文件是mp4 还是图片getFileExtension(url) {const dotIndex = url.lastIndexOf('.');const extension = url.substring(dotIndex + 1);if (/(jpg|jpeg|png)$/.test(extension.toLowerCase())) {return 'img'} else if (/(mp4|avi|mov)$/.test(extension.toLowerCase())) {return 'mp4'}return false},deleteHandle(e) {const that = thiswx.showModal({title: '',content: '是否删除',success(res) {if (res.confirm) {let obj = that.data.activityMaterialslet idx = e.currentTarget.dataset.idx //子级循环的indexobj.splice(idx, 1)that.setData({activityMaterials: obj})} else if (res.cancel) {//console.log('用户点击取消')}}})},//识别语音 -- 初始化 从这里开始是录音的内容 一直到结束initRecord: function () {var that = this;wx.onSocketOpen((res) => {// websocket打开//console.log('监听到 WebSocket 连接已打开' + res);})wx.onSocketError((err) => {//连接失败//console.log('websocket连接失败', err);wx.showToast({title: 'websocket连接失败',icon: 'none',duration: 2000,mask: false})})wx.onSocketMessage((res) => {//接收返回值var data = JSON.parse(res.data)//console.log('----------------')//console.log(data)if (data.code != 0) {//console.log("error code " + data.code + ", reason " + data.message)return}let str = ""if (data.data.status == 2) {//最终识别结果// data.data.status ==2 说明数据全部返回完毕,可以关闭连接,释放资源wxst.close();} else {//中间识别结果}iatResult[data.data.result.sn] = data.data.resultif (data.data.result.pgs == 'rpl') {data.data.result.rg.forEach(i => {iatResult[i] = null})}iatResult.forEach(i => {if (i != null) {i.ws.forEach(j => {j.cw.forEach(k => {str += k.w})})}});// console.log('这个是中间的语音识别结果')// console.log(str)that.setData({contentTxt: str //这个是中间的语音识别结果})if(!this.data.contentTxt){wx.showToast({title: '识别失败,请重新录入',icon: 'none',duration: 2000,mask: false}) return}})wx.onSocketClose((res) => {//WebSocket连接已关闭!//var that = this;recorderManager.stop();// var str = that.data.contentTxt;// console.log(str);// str = str.replace(/\s*/g, "");//去除空格// if (str.substr(str.length - 1, 1) == "。") {//去除句号// str = str.substr(0, str.length - 1);// }// console.log('这个是最后确定的语音识别结果', str)// that.setData({// contentTxt: str//这个是最后确定的语音识别结果// })//console.log('WebSocket连接已关闭!')})},//录音和停止录音操作recordHandle(e){let oactivityMaterials = this.data.activityMaterialslet parentIndex = e.currentTarget.dataset.idxthis.setData({//获取父级的下标 后面录音结束后给对应的模块赋值parentIndex: parentIndex}) //改成点击事件if(!oactivityMaterials[parentIndex].recordState){wx.showToast({title: '开始录音',icon: 'none',duration: 1000})//兄弟节点的录音按钮改成灰色if(oactivityMaterials.length > 1){for(let i in oactivityMaterials){oactivityMaterials[i].recordState = false}this.setData({activityMaterials: oactivityMaterials})}oactivityMaterials[parentIndex].recordState = truethis.setData({activityMaterials: oactivityMaterials //录音状态})wx.getSetting({//查看用户有没有开启语音权限success(res) {if (res.authSetting['scope.record']) {wx.authorize({scope: 'scope.record',success() {var xfurl = "";wx.request({//请求接口 获取讯飞语音鉴权url: BASE_URL + 'assistant/url',method: "get",header: {'content-type': 'application/json' // 默认值},success: function (res) {//console.log('鉴权结果start:');//console.log(res);if (res.statusCode == "200" && res.data) {xfurl = res.data;wxst = wx.connectSocket({ // 开启websocket连接url: xfurl,method: 'GET',success: function (res) {recorderManager.start(searchoptions);//开始录音}});} else {wx.showToast({title: '获取语音鉴权失败',icon: 'none',mask: true,duration: 3000})}},fail: function () {wx.showToast({title: '获取语音鉴权失败',icon: 'none',mask: true,duration: 3000})}})},fail() {wx.showModal({title: '微信授权',content: '您当前未开启语音权限,请在右上角设置(···)中开启“录音功能”',showCancel: false,success(res) {if (res.confirm) {//console.log('用户点击确定')}}})}})} else {wx.showModal({title: '微信授权',content: '您当前未开启语音权限,请在右上角设置(···)中开启“录音功能”',showCancel: false,success(res) {if (res.confirm) {//console.log('用户点击确定')}}})}}})}else{oactivityMaterials[parentIndex].recordState = falsethis.setData({activityMaterials: oactivityMaterials})recorderManager.stop();} },//播放操作playHandle(e){const that = thislet idx = e.currentTarget.dataset.idxlet pidx = e.currentTarget.dataset.pidx//recordArr的字段有url/txt/time/played/showTxt let activityMaterials = this.data.activityMaterialslet orecordArr = activityMaterials[pidx].recordArr[idx]innerAudioContext.src = orecordArr.url if(!orecordArr.played){//查找兄弟节点 把所有的played状态改成falsefor(let i in activityMaterials){for(let j in activityMaterials[i].recordArr){activityMaterials[i].recordArr[j].played = false}}// if(activityMaterials && activityMaterials.length > 1){// that.setData({// activityMaterials: activityMaterials// })// }//播放orecordArr.played = trueinnerAudioContext.play() // 播放 innerAudioContext.onEnded(() => {//console.log('语音播放结束'); orecordArr.played = falsethat.setData({activityMaterials: activityMaterials})});}else{//关闭orecordArr.played = falseinnerAudioContext.stop() // 停止} this.setData({activityMaterials: activityMaterials}) }})
wxss部分
.wrap{ padding: 22rpx 10rpx 100rpx 10rpx;}
.white-box{ margin-bottom: 22rpx; padding: 22rpx; width: 730rpx; box-sizing: border-box; background: #fff; border-radius: 25rpx;}
.title-box,.title-txt-part{ display: flex; align-items: center;}
.title-box{ position: relative;}
.icon-close-black{ position: absolute; right: 0; display: block; width: 20rpx; height: 21rpx;}
.icon-edit{ margin-left: 10rpx; width: 31rpx; height: 30rpx;}
.yellow-fence{ margin-right: 10rpx; width: 6rpx; height: 30rpx; background: #ffb81c; border-radius: 3rpx;}
.title-txt-part{ font-size: 28rpx; font-weight: bold;}
.title-input{ padding-left: 16rpx; width: 360rpx; height: 60rpx; border:1rpx solid #ddd; border-radius: 10rpx; font-size: 26rpx;}.material-box{ margin-top: 22rpx; display: flex; justify-content: flex-start; flex-wrap: wrap;}
.upload-box{ margin: 0 25rpx 25rpx 0; padding: 0; display: flex; justify-content: center; align-items: center; width: 310rpx; height: 186rpx; background: #f0f0f0; font-size: 25rpx; color: #111; border: 0; border-radius: 22rpx;}
button::after {border: none;}
.icon-photo{ margin-right: 10rpx; display: block; width: 93rpx; height: 85rpx;}
.material-list{ position: relative; margin: 0 25rpx 25rpx 0; width: 310rpx; height: 186rpx;}
.material-list-m2{ width: 100%; height: 100%; border-radius: 22rpx;}
.icon-close{ position: absolute; top: 10rpx; right: 10rpx; z-index: 999; display: block; width: 50rpx; height: 50rpx;}
.add-box{ margin: 22rpx auto 0 auto; width: 270rpx; text-align: center; line-height: 60rpx; font-size: 35rpx; color: #ffb81c; font-weight: bold; border-bottom: 3rpx solid #ffb81c;}
.save-btn{ margin: 100rpx auto 0 auto; width: 330rpx; height: 84rpx; text-align: center; line-height: 84rpx; background: #ffb81c; font-size: 30rpx; color: #111; font-weight: bold; border-radius: 42rpx;}.textarea-box-wrap{ margin-top: 22rpx; padding: 22rpx; display: flex; flex-direction: column; justify-content: center; height: 580rpx; background: #f0f0f0; border-radius: 25rpx;}
.textarea-box{ flex:1; height: 100%; box-sizing: border-box; font-size: 25rpx; color: #111;}
.voice-box{ margin-bottom: 20rpx; max-height: 200rpx; overflow-y: scroll; box-sizing: border-box; font-size: 25rpx; color: #111;}
.videoBtn {width: 50%;}
.videoBtn text{color:#fff;}
.videoBtnBg {background: #bdb4b4;}
.startYuyinImage { position: fixed;top: 200rpx;left: 50%;z-index: 99;transform: translateX(-50%);display:flex;justify-content:center;flex-flow: column;width: 220rpx;height: 220rpx;text-align: center;background: rgba(0, 0, 0, 0.1); color: #fff;font-size: 24rpx;border-radius: 50%;
}
.startYuyinImage cover-image { margin:0 auto; width:80rpx;height: 80rpx;}
.startYuyinImage cover-view { margin-top: 22rpx;}
.musicd {position: fixed;z-index: 9999;top: 10%;left: 4%;
}
.musicd image {width: 100rpx;height: 100rpx;z-index: 999;
}.icon-record{ display: block; width: 70rpx; height: 70rpx;}
.record-btn{ margin: 0 auto; padding: 0; width: 70rpx; height: 70rpx; background: transparent; border-radius: 50%;}
button::after{border: none;}
.record-list{ padding-left: 18rpx; display: flex; align-items: center; box-sizing: border-box; width: 640rpx; height: 74rpx; background: #fff; color: #19b2ff; border-radius: 18rpx;}
.icon-video{ display: block; width: 56rpx; height: 55rpx;}
.record-list .t1{ margin: 0 22rpx;}
.record-txt{ padding:10rpx 10rpx 0 10rpx; font-size: 24rpx; color: #19b2ff;}
.record-item{ margin-bottom: 22rpx;}
.record-item:last-child{ margin-bottom: 0;}
json不用调用什么
页面我也还没写完 单纯的记录一下这个功能
这篇关于微信小程序调用讯飞接口实现录音和获取录音音译的文本 前端部分 包含图片、视频的上传功能的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!