import { getreadInfo } from '~/api/video' import { createStoreBindings } from 'mobx-miniprogram-bindings' import { store } from '~/store/index' const aiengine = require('~/utils/ChivoxAiEngine') const sha1 = require('~/utils/sha1'); // 文章行高 let rowH = 0 let videoContext = null // 滚动变色定时器 let stl = null // 倒计时 let setTimeoutObj = null // 录音 let innerAudioContext = null let resultAudioContext = null /*创建基础引擎*/ let wsEngine = aiengine.createWsEngine({}); /*微信录音*/ let recorderManager = wx.getRecorderManager(); Page({ data: { videoInfo: {}, currentRow: null, state: false, countDown: { state: false, num: 3, }, contentH: 0, scrollTop: 0, //如果readingReset为true就是重读 readingReset: false, //readingType为public是普通阅读,为pk是pk逻辑 readingType: 'public', article: [{ id: 1, text: '传说在很久很久以前,', time: '0' }, { id: 2, text: '天和地还没有分开,', time: '4010' }, { id: 3, text: '整个宇宙混沌一团,', time: '7080' }, { id: 4, text: '像个大鸡蛋。', time: '10150' }, { id: 5, text: '有个叫盘古的大神,', time: '13150' }, { id: 6, text: '昏睡了一万八千年。', time: '16190' }, { id: 7, text: '一天,大神醒来,睁眼一看,', time: '20030' }, { id: 8, text: '周围黑乎乎一片,', time: '24210' }, { id: 9, text: '什么也看不见。', time: '27300' }] }, onLoad(options) { let videoId = options.videoId this.getreadInfo(videoId) let data = this.data.article data = data.map((item, index) => { item.readTime = data[index + 1] ? data[index + 1].time - item.time : '' return item }) this.setData({ article: data, readingReset: options.reset || false, readingType: options.readingType || 'public' }) // 手工绑定 this.storeBindings = createStoreBindings(this, { store, fields: { userInfo: 'userInfo', readDetail: 'readDetail' }, actions: { setReadDetail: 'setReadDetail' } }) if (!options.reset) { this.getHeight() } // 录音授权 wx.getSetting({ success(res) { if (!res.authSetting['scope.record']) { wx.authorize({ scope: 'scope.record', success() { // 用户已经同意小程序使用录音功能,后续调用接口不会弹窗询问 wx.getRecorderManager() } }) } } }) /*监听评测结果:必须在基础引擎创建后,调用任何评测接口前设置监听,否则有可能收不到相关事件。*/ wsEngine.onResult((res) => { this.getRecordScore(res) }); wsEngine.onErrorResult((res) => { console.log("===收到错误结果=============", res) }); }, // 获取阅读内容 async getreadInfo(videoId) { let videoInfo = await getreadInfo(videoId) wx.setNavigationBarTitle({ title: videoInfo.userRead.title }) this.setData({ videoInfo }) if (!this.data.videoInfo.userReadExtend) { this.videoContext = wx.createVideoContext('myVideo') } else { this.innerAudioContext = wx.createInnerAudioContext(); this.innerAudioContext.src = videoInfo.userRead.audioPath this.innerAudioContext.onEnded(res => { this.finishRecord() }) this.innerAudioContext.onStop((res) => { this.finishRecord() }); } }, // 开始录制 setCountDown() { if (this.data.state) { this.finishRecord() return } if (this.data.readingReset) { this.clearReset() this.getHeight() } this.setData({ 'countDown.state': true }) this.stl = setInterval(() => { if (this.data.countDown.num == 0) { clearInterval(this.stl) this.setData({ state: true, countDown: { state: false, num: 3 } }) this.soundRecording() this.playMediaState() this.startRecording() } else { this.setData({ 'countDown.num': --this.data.countDown.num }) } }, 1000) }, // 录音 soundRecording() { /*调用微信开始录音接口,并启动语音评测*/ let timeStamp = new Date().getTime() let sig = sha1(`16075689600000da${timeStamp}caa8e60da6042731c230fe431ac9c7fd`) let app = { applicationId: '16075689600000da', sig, //签名字符串 alg: 'sha1', timestamp: timeStamp + '', userId: wx.getStorageSync('uid') } let lessonText = this.data.videoInfo.userRead.lessonText; wsEngine.start({ request: { coreType: "cn.pred.raw", refText: lessonText, rank: 100, attachAudioUrl: 1, result: { details: { gop_adjust: 1 } } }, app, audio: { audioType: "mp3", channel: 1, sampleBytes: 2, sampleRate: 16000 }, success: (res) => { /*引擎启动成功,可以启动录音机开始录音,并将音频片传给引擎*/ const options = { sampleRate: 44100, //采样率 numberOfChannels: 1, //录音通道数 encodeBitRate: 192000, //编码码率 format: 'mp3', //音频格式,有效值aac/mp3 frameSize: 50 //指定帧大小,单位 KB }; //开始录音,在开始录音回调中feed音频片 recorderManager.start(options); }, fail: (res) => { console.log("fail============= " + res); }, }); //监听录音开始事件 recorderManager.onStart(() => {}); //监听录音结束事件 recorderManager.onStop((res) => { console.log('录音结束', res); this.setData({ tempFilePath: res.tempFilePath, }); //录音机结束后,驰声引擎执行结束操作,等待评测返回结果 wsEngine.stop({ success: () => { console.log('====== wsEngine stop success ======'); }, fail: (res) => { console.log('录音结束报错', res); }, }); }); //监听已录制完指定帧大小的文件事件。如果设置了 frameSize,则会回调此事件。 recorderManager.onFrameRecorded((res) => { const { frameBuffer } = res //TODO 调用feed接口传递音频片给驰声评测引擎 wsEngine.feed({ data: frameBuffer, // frameBuffer为微信录音机回调的音频数据 success: () => { console.log('feed success.监听已录制完指定帧大小的文件事件') }, fail: (res) => { console.log('监听已录制完指定帧大小报错', res) }, }); }); }, // 结束录制 finishRecord() { recorderManager.stop(); this.stopMediaState() clearTimeout(this.setTimeoutObj) clearInterval(this.stl) this.setData({ state: false, currentRow: null, scrollTop: 0 }) }, // 测试的 pkResult() { wx.redirectTo({ url: `/pages/pkResult/index`, }) }, // 字体换行 startRecording() { if (this.data.currentRow == null) { this.setData({ currentRow: 0 }) } let row = this.data.article[this.data.currentRow] if (!row.readTime) { return } this.setTimeoutObj = setTimeout(() => { this.setData({ currentRow: ++this.data.currentRow }) this.setData({ scrollTop: this.rowH * this.data.currentRow }) this.startRecording() }, row.readTime); }, // 视频播放结束 videoEnd() { this.finishRecord() }, // 获取测评结果 getRecordScore(res) { const result = res.result; const integrity = Math.floor(result.integrity); //完成度 const tone = Math.floor(result.tone); // 语调声调 const accuracy = Math.floor(result.overall); // 准确度 发音分 const fluency = Math.floor(result.fluency.overall); //流利度 let myOverall = Math.floor(integrity * 0.3 + accuracy * 0.5 + fluency * 0.1 + tone * 0.1); let detail = { integrity, tone, accuracy, fluency, myOverall, tempFilePath: this.data.tempFilePath, title: this.data.videoInfo.userRead.title, id: this.data.videoInfo.userRead.exampleId, } this.setReadDetail(detail) wx.redirectTo({ url: this.data.readingType == 'public' ? '/pages/score/index' : '/pages/pkResult/index', }) }, videoPlay() { if (this.data.readingReset) { this.resultAudioContext = wx.createInnerAudioContext(); this.resultAudioContext.src = this.data.readDetail.tempFilePath; // 这里可以是录音的临时路径 this.resultAudioContext.play(); } }, // 清除试听状态 clearReset() { if (this.resultAudioContext) { this.resultAudioContext.stop() } this.setData({ readingReset: false }) }, // 控制视频或音频的播放状态 playMediaState() { if (!this.data.videoInfo.userReadExtend) { this.videoContext.play() } else { this.innerAudioContext.play(); } }, // 控制视频或音频的暂停状态 stopMediaState() { if (!this.data.videoInfo.userReadExtend) { this.videoContext.stop() this.videoContext.seek(0) } else { this.innerAudioContext.stop() } }, // 获取设备高度与行高度 getHeight() { var query = wx.createSelectorQuery(); query.select('.content').boundingClientRect((rect) => { this.setData({ contentH: rect.height }) }).exec() query.select('.row').boundingClientRect((rect) => { this.rowH = rect.height }).exec() }, /** * 生命周期函数--监听页面卸载 */ onUnload() { wsEngine.reset() recorderManager.stop(); if (this.innerAudioContext) { this.innerAudioContext.stop() } clearTimeout(this.setTimeoutObj) clearInterval(this.stl) }, })