import { Snackbar } from "@varlet/ui"; import { closeToast, showLoadingToast } from "vant"; import { defineComponent, onBeforeUnmount, onMounted, onUnmounted, reactive, ref, watch } from "vue"; import { getLeveByScore, getLeveByScoreMeasure, IEvaluatings } from "./evaluatResult"; import { cancelEvaluating, endEvaluating, endSoundCheck, getEarphone, api_proxyServiceMessage, removeResult, sendResult, startEvaluating, startSoundCheck, api_openWebView, api_startRecording, api_stopRecording, api_recordStartTime, api_remove_recordStartTime, api_videoUpdate, api_startCapture, api_endCapture, } from "/src/helpers/communication"; import state, { IPlayState, clearSelection, handleStopPlay, resetPlaybackToStart, togglePlay } from "/src/state"; import { IPostMessage } from "/src/utils/native-message"; import { usePageVisibility } from "@vant/use"; import { browser } from "/src/utils"; import { getAudioCurrentTime, setAudioCurrentTime, toggleMutePlayAudio } from "../audio-list"; import testAudio from './testAudio.mp3' const browserInfo = browser(); export const evaluatingData = reactive({ /** 评测数据 */ contentData: {} as any, /** 评测模块是否加载完成 */ rendered: false, earphone: false, // 是否插入耳机 soundEffect: false, // 是否效音 soundEffectFrequency: 0, // 效音频率 checkStep: 0, // 执行步骤 checkEnd: false, // 检测结束 earphoneMode: false, // 耳机弹窗 soundEffectMode: false, // 效音弹窗 websocketState: false, // websocket连接状态 /**是否开始播放 */ startBegin: false, // 开始 backtime: 0, // 延迟时间 /** 已经评测的数据 */ evaluatings: {} as IEvaluatings, /** 评测结果 */ resultData: {} as any, /** 评测结果弹窗 */ resulstMode: false, /** 是否是完整评测 */ isComplete: false, }); /** 点击开始评测按钮 */ export const handleStartEvaluat = () => { if (state.modeType === "evaluating") { handleCancelEvaluat(); } else { handleStopPlay(); } state.modeType = state.modeType === "evaluating" ? "practise" : "evaluating"; if (state.modeType !== "evaluating") { // 切换到练习模式,卸载评测模块 evaluatingData.rendered = false; } }; const check_currentTime = () => { let preTime = 0; // 选段评测模式 if (state.isSelectMeasureMode) { preTime = state.section[0].time * 1000; } const currentTime = getAudioCurrentTime() * 1000 - preTime; // console.log('播放进度music', currentTime, 'preTime:' + preTime) if (currentTime >= 500) { sendEvaluatingOffsetTime(500); return; } setTimeout(() => { check_currentTime(); }, 10); }; /** 开始播放发送延迟时间 */ export const sendEvaluatingOffsetTime = async (currentTime: number) => { // 没有开始时间点, 不处理 if (!evaluatingData.backtime) return; const nowTime = Date.now(); const delayTime = nowTime - evaluatingData.backtime - currentTime; console.error("真正播放延迟", delayTime, "currentTime:", currentTime); await api_proxyServiceMessage({ header: { commond: "audioPlayStart", type: "SOUND_COMPARE", }, body: { offsetTime: delayTime < 0 ? 0 : delayTime, }, }); }; /** 检测耳机 */ const checkUseEarphone = async () => { const res = await getEarphone(); return res?.content?.checkIsWired || false; }; /** * 开始录音 */ const handleStartSoundCheck = () => { startSoundCheck(); }; /** 结束录音 */ export const handleEndSoundCheck = () => { endSoundCheck(); }; /** 连接websocket */ export const connectWebsocket = async (content: any) => { evaluatingData.contentData = content; evaluatingData.websocketState = true; }; /** * 执行检测 */ export const handlePerformDetection = async () => { evaluatingData.checkEnd = false; if (evaluatingData.checkStep === 0) { // 检测耳机 const erji = await checkUseEarphone(); evaluatingData.checkStep = 1; if (erji) { handlePerformDetection(); } else { evaluatingData.earphoneMode = true; } return; } if (evaluatingData.checkStep === 1) { // 效音 // 是否需要开启效音 evaluatingData.checkStep = 10; if (state.setting.soundEffect) { evaluatingData.soundEffectMode = true; handleStartSoundCheck(); } else { handlePerformDetection(); } return; } if (evaluatingData.checkStep === 10) { // 连接websocket evaluatingData.checkEnd = true; evaluatingData.checkStep = 0; } }; /** 记录小节分数 */ export const addMeasureScore = (measureScore: any, show = true) => { evaluatingData.evaluatings[measureScore.measureRenderIndex] = { ...measureScore, leve: getLeveByScoreMeasure(measureScore.score), show, }; // console.log("🚀 ~ measureScore:", evaluatingData.evaluatings[measureScore.measureRenderIndex]) }; const handleScoreResult = (res?: IPostMessage) => { if (res?.content) { console.log("🚀 ~ 评测返回:", res); const { header, body } = res.content; // 效音返回 if (header.commond === "checking") { evaluatingData.soundEffectFrequency = body.frequency; } // 小节评分返回 if (header?.commond === "measureScore") { addMeasureScore(body); } // 评测结束返回 if (header?.commond === "overall") { // console.log("评测结束", body); evaluatingData.resulstMode = true; evaluatingData.resultData = { ...body, ...getLeveByScore(body.score), }; // console.log("🚀 ~ evaluatingData.resultData:", evaluatingData.resultData) closeToast(); } } }; /** 开始评测 */ export const handleStartBegin = async () => { evaluatingData.isComplete = false; evaluatingData.startBegin = true; evaluatingData.evaluatings = {}; evaluatingData.resultData = {}; evaluatingData.backtime = 0; resetPlaybackToStart(); try { // console.log("🚀 ~ content:", evaluatingData.contentData, JSON.stringify(evaluatingData.contentData)); } catch (error) {} const res = await startEvaluating(evaluatingData.contentData); if (res?.api !== "startEvaluating") { Snackbar.error("请在APP端进行评测"); evaluatingData.startBegin = false; return; } // 开始录音 api_startRecording(); }; /** 播放音乐 */ const playMusic = async () => { const playState = await togglePlay("play"); // 取消播放,停止播放 if (!playState) { evaluatingData.startBegin = false; handleCancelEvaluat(); return; } // 检测播放进度, 计算延迟 check_currentTime(); // 如果开启了摄像头, 开启录制视频 if (state.setting.camera && state.setting.saveToAlbum) { console.log("开始录制视频"); api_startCapture(); } }; let _audio: HTMLAudioElement; /** 录音开始,记录开始时间点 */ const recordStartTimePoint = async (res?: IPostMessage) => { // 没有开始评测,不处理 if (!evaluatingData.startBegin) return; let inteveral = res?.content?.inteveral || 0; if (browserInfo.ios) { inteveral *= 1000; } evaluatingData.backtime = inteveral || Date.now(); console.log("🚀 ~ 开始时间点:", evaluatingData.backtime, "已经录的时间:", Date.now() - inteveral, '记录时间点:', Date.now()); // 开始播放 playMusic(); // _audio.play(); }; const getTestCurrent = () => { const _c = _audio.currentTime * 1000 console.log("🚀 ~ 播放的时间测试:", _c) if (_c >= 500){ console.log('evaluatingData.backtime: ', evaluatingData.backtime) console.error('开始播放的延迟:', Date.now() - evaluatingData.backtime - 500) // playMusic() _audio.pause() return } setTimeout(() => { getTestCurrent() }, 10) } const playTestMusic = () => { // _audio = new Audio(state.music) _audio = new Audio(testAudio) // _audio.muted = true // _audio.src = testAudio _audio.onplay = () => { console.log('开始播放测试') getTestCurrent() } _audio.onloadedmetadata = () => { console.log('测试音频加载完成') // _audio.play(); } _audio.load() } /** * 结束评测 * @param isComplete 是否完整评测 * @returns */ export const handleEndEvaluat = (isComplete = false) => { // 没有开始评测 , 不是评测模式 , 不评分 if (!evaluatingData.startBegin || state.modeType !== "evaluating") return; evaluatingData.startBegin = false; // 结束录音 api_stopRecording(); // 结束评测 endEvaluating({ musicScoreId: state.examSongId, }); showLoadingToast({ message: "评分中", duration: 0, forbidClick: true, }); evaluatingData.isComplete = isComplete; // 如果开启了摄像头, 结束录制视频 if (state.setting.camera && state.setting.saveToAlbum) { console.log("结束录制视频"); api_endCapture(); } }; /** * 结束评测 */ export const handleEndBegin = () => { handleEndEvaluat(); handleStopPlay(); }; /** * 取消评测 */ export const handleCancelEvaluat = () => { evaluatingData.evaluatings = {}; evaluatingData.startBegin = false; // 关闭提示 closeToast(); // 取消记录 api_proxyServiceMessage({ header: { commond: "recordCancel", type: "SOUND_COMPARE", status: 200, }, }); // 取消评测 cancelEvaluating(); // 停止播放 handleStopPlay(); }; /** 查看报告 */ export const handleViewReport = (key: "recordId" | "recordIdStr", type: "gym" | "colexiu" | "orchestra") => { const id = evaluatingData.resultData?.[key] || ""; let url = ""; switch (type) { case "gym": url = location.origin + location.pathname + "#/report/" + id; break; case "orchestra": url = location.origin + location.pathname + "report-share.html?id=" + id; break; default: url = location.origin + location.pathname + "report-share.html?id=" + id; break; } api_openWebView({ url, orientation: 0, isHideTitle: true, // 此处兼容安卓,意思为隐藏全部头部 statusBarTextColor: false, isOpenLight: true, }); }; export default defineComponent({ name: "evaluating", setup() { const pageVisibility = usePageVisibility(); // 需要记录的数据 const record_old_data = reactive({ /** 指法 */ finger: false, /** 原音伴奏 */ play_mode: '' as IPlayState, /** 评测是否要伴奏 */ enableAccompaniment: true, }); /** 记录状态 */ const hanlde_record = () => { // 取消指法 record_old_data.finger = state.setting.displayFingering; state.setting.displayFingering = false; // 切换为伴奏 record_old_data.play_mode = state.playSource record_old_data.enableAccompaniment = state.setting.enableAccompaniment // 如果关闭伴奏,评测静音 if (!record_old_data.enableAccompaniment){ toggleMutePlayAudio(record_old_data.play_mode === 'music' ? 'music' : 'background', true) } }; /** 还原状态 */ const handle_reduction = () => { // 还原指法 state.setting.displayFingering = record_old_data.finger; state.playSource = record_old_data.play_mode // 如果关闭伴奏, 结束评测取消静音 if (!record_old_data.enableAccompaniment){ toggleMutePlayAudio(record_old_data.play_mode === 'music' ? 'music' : 'background', false) } }; watch(pageVisibility, (value) => { if (value == "hidden" && evaluatingData.startBegin) { handleEndBegin(); } }); onMounted(() => { hanlde_record(); evaluatingData.resultData = {}; // evaluatingData.resultData = {...getLeveByScore(90), score: 30, intonation: 10, cadence: 30, integrity: 40} // console.log("🚀 ~ evaluatingData.resultData:", evaluatingData.resultData) evaluatingData.evaluatings = {}; evaluatingData.soundEffectFrequency = 0; evaluatingData.checkStep = 0; evaluatingData.rendered = true; sendResult(handleScoreResult); api_recordStartTime(recordStartTimePoint); // 不是选段模式评测, 就清空已选段 if (!state.isSelectMeasureMode) { clearSelection(); } console.log("加载评测模块成功"); }); onUnmounted(() => { removeResult(handleScoreResult); api_remove_recordStartTime(recordStartTimePoint); handle_reduction(); console.log("卸载评测模块成功"); }); return () =>
; }, });